导航: 老古网老古论坛XMOS公共讨论区XMOS开源项目区单片机程序设计嵌入式系统广告区域
→发表看法:[因果]基于8位加法的初等函数计算器设计



No.53592
作者:因果
邮件:chenshiyangyi@163.com
ID:113570
登陆:7次
文章数:11篇
最后登陆IP:119.59.248.166
最后登陆:2011/10/3 17:36:40
注册:2008/6/11 14:23:51
财富:156
发帖时间:2008/7/20 16:55:38
发贴者IP:219.133.174.123
标题:因果:基于8位加法的初等函数计算器设计
摘要:No.53592基于8位加法的初等函数计算器设计 基于8位加法的初等函数计算器设计
2004年初,我在一家台资小公司上班,公司老板想在电子行业有一个持久的,产品来维系公司的资金来源,开始着手于计算器的研制。最后把这个任务分派给了我,由我写出初等函数,多项表达式由老板自己写。于是我开始一项痛苦的摸索过来,有两上问题我必须解决:
1. 成本问题(即要达到高精度,也要考虑成本问题,主芯片选用6502,下Mask 2-3元人发币,工程可实现性,计算一个初等函数最多允许1/20秒).
2. 计算方法,在上大学的时候用的是泰勒级数,但是展开式太多了,是不是别他国外的公司也是用这个级数展开了,这是一个问题。
我看了许多的国外的网站,讲到了卡西欧 CASIO的设计,但是也没有说到具体的底层应该怎样构建。我查阅了一些关于数据计算方法的书籍,其中有一个叫密比雪夫多项式的数学方法吸引了我,直觉告诉我,这就是我要找到的。后来计算器的设计,我用的是密比雪夫多项式的系数,而不是用泰勒级数展开得的系数。
整个程序的过程是基于8位的加减法进行的,浮点数是BCD码。我把它做成了Class,用C++编写,其实也同struct 一样的。对于浮点数的乘除法,我采用了建倍数表的方法。
设计的整过程序是基于{AddSub8. ADC8();AddSub8.SBC8()}8位的加减法去模拟6502,
用它搭建浮点数的加减乘除: FPAddSub(),FPMul();FPDiv();其中Cchebft  用于计算多项式的系数.
笔者把这几年收集的关于国人对计算器的研究文章”计算器的计算误差与计算精确度的研讨”,”怎样用没有开立方根按键的计算器开立方根和高次方根”等等文件及计算器源程序的工程文件放置网站: ,如果您感兴趣,请去下载。如有疑问也可以来电,我们一起讨论:chenshiyangyi@163.com;QQ:3637323.
还有一个问题,就是对数函数,并不是一直都用切比雪夫系数计算,在一定泛围,用了帕德函数计算。具体的方法,可能要做适当的改动.

源程序如下:
#include   <stdio.h>  
#include   <conio.h>  
#include   <stdlib.h>  
#include   <math.h>  
#define CHM 7
#define ENTER printf("\n");
#define PI 3.141592653589792384626433832795

//程序有一些问题:可能在运算前先判断末尾连续为零的个数可提高效率

 class AddSub8
{
 public:
         bool  c;//进位、借位标志
         short ADC8(short,short);//8bits相加
         short SBC8(short,short);//8bits相减
         void  CLC(){c=0;};
         void  SEC(){c=1;};
};
 short AddSub8::ADC8(short a1,short a2)
 {
  short result;
  if(c==true)  result=a1+a2+1;
  else result=a1+a2;
  if(result>  99)  { c=1;return result-100;}
  else    {c=0;return result;}
 }
 short AddSub8::SBC8(short a1,short a2)
 {
 short result;
 if(c==true)  result=a1-a2;
 else result=a1-a2-1;
 if(result  <0) {c=0;return result+100;}
 else {c=1;return result;}
 };


class FP
{
 public:
        bool  Sign;//符号 (1)为负
        short  Exponenet;//以二进制形式表示的指数
        short Mantissa[CHM];//CHM BCD码形式的尾数
  void  Equ(short *data) {for(int i=0;i  <CHM;i++) Mantissa[i]=data[i];}
  //    bool  ExponenetSign()   {if((Exponenet&128)>  0) return true;  return false;}
  FP& operator =(FP & fp)
  { 
   Sign=fp.Sign;
   Exponenet=fp.Exponenet;
   for(int i=0;i  <CHM;i++) Mantissa[i]=fp.Mantissa[i];
   return *this;}
     friend FP  operator +(FP,FP);
  friend FP  operator -(FP,FP);
  friend FP  operator *(FP,FP);
  friend FP  operator /(FP,FP);
  
       
};

FP operator +(FP num1,FP num2)
{
FP num3;
void FPAddSub(FP,FP,FP&);
FPAddSub(num1,num2,num3);
return num3; 
}
FP operator -(FP num1,FP num2)
{
FP num3;
void FPAddSub(FP,FP,FP &);
num2.Sign=!num2.Sign;
FPAddSub(num1,num2,num3);
return num3;
}
 FP operator *(FP num1,FP num2)
 {
  FP num3;
  void FPMul(FP,FP,FP&);
  FPMul(num1,num2,num3);
  return num3;
 }
 FP operator /(FP num1,FP num2)
 {
 FP num3;
 bool FPDiv(FP,FP,FP&);
 FPDiv(num1,num2,num3);
 return num3;
 }
class Vector
{
public:
   void PrintfShort(short *data,int n,bool flag=true)     
   {if(flag==true) printf("\n");
  for(int i=0;i  <n;i++)  
  { if(data[i]==0) {printf("00");continue;}
   if(data[i]  <10) printf("0");
         printf("%d",data[i]);
  }
   }
    void BCDLeftShift(short *,short *,int);
 void BCDLeftShift(short *,int);
 void PrintfFp(FP);
 void Equ(short *data1,short *data2,int n){for(int i=0;i  <n;i++) data1[i]=data2[i];}
 void PrintZero(int n){for(int i=0;i  <n;i++) printf("0");}
 void ClrFP(FP &data)  {data.Exponenet=0;data.Sign=false;for(int i=0;i  <CHM;i++) data.Mantissa[i]=0;}
    void ClrShort(short *data,int n){for(int i=0;i  <n;i++) data[i]=0;}
 void BCD8ToBCD4(short *,int,short *);
 void BCDRightShift(short *,int);//左移一位,高BCD移出,末低BCD添零
 double FP2Double(FP);//把FP数据类型转化为double型
 void   Double2FP(double,FP&);
 FP     Double2FP(double);
 void   Norm(FP &);
 void PrintfDouble(double *,int);
 
};
  FP Vector::Double2FP(double a)
  {
  FP num1;
  Double2FP(a,num1);
  return num1;
  }
   void Vector::PrintfDouble(double *data,int num)
   {
   for(int i=0;i  <num;i++) printf("%.15e\n",data[i]);
   }
   void Vector::Norm(FP &x)
   {
   for(int i=0;i  <2*CHM;i++)
    if(x.Mantissa[0]/10==0) {x.Exponenet--;BCDRightShift(x.Mantissa,CHM);} 
     else 
     break;
   if(i==2*CHM) x.Exponenet=0;
   }
  void Vector::Double2FP(double a,FP &fp)
  {
     if(a>  0) fp.Sign=false;
     else
  {
  fp.Sign=true;
  a=fabs(a);
  }
  fp.Exponenet=0;
  
  if(a  <1e-110) {ClrFP(fp);return;}
  int exp=0;
     for(int i=0;i  <110;i++)
  {
   if(floor(a)==0) {a*=10;fp.Exponenet--;}
   else
   {
   a/=10;fp.Exponenet++;
   }
      if(floor(a)>  =1 && floor(a)  <=9) break; 
  }
     a=a*10;

       for(i=0;i  <CHM;i++)
    {
     fp.Mantissa[i]=(short)floor(a);
        a=(a-floor(a))*100;
    
    }
   
  }
  double Vector::FP2Double(FP num)
  {
    double a=0,b=10;
    for(int i=0;i  <CHM;i++)
    {
 a+=num.Mantissa[i]/b;
 b*=100;
 }
 a*=pow(10,num.Exponenet);
 if(num.Sign==false) return a;
 else
  return a*(-1);
  }
void Vector::BCDRightShift(short *num,int n)//左移一位BCD码
{
    for(int i=0;i  <n-1;i++)
  num[i]=(num[i]%10)*10+num[i+1]/10;
 num[n-1]=(num[n-1]%10)*10;
}
void Vector::BCDLeftShift(short *num,int n)
{
    
 for(int i=n-1;i>  =1;i--)
        num[i]=num[i]/10+(num[i-1]%10)*10;
    num[0]=num[0]/10;
}

void Vector::BCD8ToBCD4(short *num1,int n,short *num)
{
      for(int i=0;i  <n;i++)
   {
   num[2*i]=num1[i]/10;//在汇编程序的实现: &0xF0
   num[2*i+1]=num1[i]%10;//&0x0F
   }

}
void Vector::BCDLeftShift(short *num1,short *num2,int n)
 {
  short a=0,b=0;
 for(int i=0;i  <n;i++)
 {
 a=num1[i]/10;
 num2[i]=b*10+a;
    b=num1[i]%10;
 }
    num2[n]=b*10;
 };
void Vector::PrintfFp(FP data)
{
if(data.Sign==true) printf("-");
printf("%d.%d",data.Mantissa[0]/10,data.Mantissa[0]%10);
 for(int i=1;i  <CHM;i++) 
 {
  if(data.Mantissa[i]  <=9) printf("0%d",data.Mantissa[i]);
  else
  printf("%d",data.Mantissa[i]);
 
 }   
printf("E%d",data.Exponenet);
}
 class FileFP
 {
 public:
       void SaveDouble(double);
       void SaveFP(FP);
    void SaveCoAsm(FP);
    FP   ReadFP();
       FileFP(char *,bool);
       ~FileFP(){fclose(fp);}

 private:
    FILE *fp;

 };
 FileFP::FileFP(char *str,bool flag=true)
 {
  if(flag==true)  fp=fopen(str,"w");
  else
   fp=fopen(str,"r");
  if(fp==NULL) exit(0);
 }
 FP FileFP::ReadFP()
 {
 char c1,c2;
 short n=0,n1,n2=0;
 FP x;
 Vector vect;  vect.ClrFP(x);
 c1=getc(fp);
 while(c1  <'0' || c1>  '9')
 {
     if(c1=='-') x.Sign=true;
  c1=getc(fp);
 }
 c2=getc(fp);//小数点

 while(c1!='e' && c1!='E')
 {
 c2=getc(fp);
 x.Mantissa[n]=(c1-'0')*10+c2-'0';
 n++;
 c1=getc(fp);
 }

 fscanf(fp,"%d",&n1);
 x.Exponenet=n1;
 return x;
 }

 void FileFP::SaveDouble(double x)
 {
 fprintf(fp,"%.13e\n",x);
 }
 void FileFP::SaveFP(FP x)
 {
 if(x.Sign==true) fprintf(fp,"-");
 fprintf(fp,"%d.%d",x.Mantissa[0]/10,x.Mantissa[0]%10);
 for(int i=1;i  <CHM;i++) 
 {
  if(x.Mantissa[i]  <=9) fprintf(fp,"0%d",x.Mantissa[i]);
  else
  fprintf(fp,"%d",x.Mantissa[i]);
 }   
fprintf(fp,"e%d\n",x.Exponenet);
 }
         
 void FileFP::SaveCoAsm(FP x)
 {
 fprintf(fp,"\n                   .DB %d",x.Exponenet);
 if(x.Sign==true) fprintf(fp,",1");
 else
  fprintf(fp,",0");
 for(int i=0;i  <CHM;i++)
  if(x.Mantissa[i]  <=9) fprintf(fp,",$0%d",x.Mantissa[i]);
  else
   fprintf(fp,",$%d",x.Mantissa[i]);
 
 }


void FPAddSub(FP num1,FP num2,FP & num3)
{
 short shift=0,i;
 bool  flag=false;//false 表示num1为主运算区,num2进行移位操作 ,c表示有无进位
 short num[CHM+2];//进行移位对齐时应该注意任零两加数都不应该为零
 if(num1.Mantissa[0]==0) {num3=num2;return;}
 if(num2.Mantissa[0]==0) {num3=num1;return;}//不是很规范;判零
 num[CHM]=0;
    if(num1.Exponenet==num2.Exponenet) //指数相等,比较尾数
 {
  for(i=0;i  <CHM;i++)
  { if(num1.Mantissa[i]==num2.Mantissa[i])  continue;
  if(num1.Mantissa[i]>  num2.Mantissa[i])   flag=false;
  else
   flag=true;
  break;
 }
 }
    else//指数不相等,比较指数
 {
  shift=num1.Exponenet-num2.Exponenet;
  if(shift>  0) flag=false;
  else
  {
   flag=true;shift=shift*(-1);
  }
 }
//确定主运算区(num3),加数(num)
Vector vect;

  if(flag==false) 
  {num3=num1;
  if(shift%2==0)   vect.Equ(num,num2.Mantissa,CHM);
  else 
      vect.BCDLeftShift(num2.Mantissa,num,CHM);//BCD右移一位
  }
 else
 {num3=num2;
 if(shift%2==0)
 vect.Equ(num,num1.Mantissa,CHM);
 else
   vect.BCDLeftShift(num1.Mantissa,num,CHM);//BCD码右移一位
 }
AddSub8 add;


shift=shift/2; 
if(shift>  CHM+1) return;
short   *result;
result=new short[4*CHM];//可能太浪费空间
vect.ClrShort(result,3*CHM);
        if((num1.Sign^num2.Sign)==false)  //加法
  {
  add.CLC();
  for(i=shift+CHM;i>  =0;i--) 
        if(i>  =CHM) result[i+1]=add.ADC8(0,num[i-shift]); //有几步是多余的 
     else if(i  <shift) result[i+1]=add.ADC8(num3.Mantissa[i],0);
     else
   result[i+1]=add.ADC8(num3.Mantissa[i],num[i-shift]);
 
     result[0]=add.ADC8(0,0);//以上完成了数值的加法
           if(result[0]==0) 
      vect.Equ(num3.Mantissa,result+1,CHM);//没有进位
     else//否则右移一位
     {
     num3.Exponenet++;
     //vect.BCDLeftShift(result+1,num3.Mantissa,CHM-1);//一次左移更干净
           //num3.Mantissa[0]+=10*result[0];//???
     
     vect.BCDRightShift(result,CHM+1);
           vect.Equ(num3.Mantissa,result,CHM);

     }
  
     delete []result;
     return;
  }        
  else//减法
  {
  add.SEC();
  for(i=shift+CHM;i>  =0;i--) 
        if(i>  =CHM) result[i]=add.SBC8(0,num[i-shift]);  //有几步是多余的:
                                                     //至少不应该保存起来(没有多余,有可能前面几位为零)
                                                    //即使这种情况没有,但可以为后续的开发利用
     else if(i  <shift) result[i]=add.SBC8(num3.Mantissa[i],0);
     else
      result[i]=add.SBC8(num3.Mantissa[i],num[i-shift]);
        
     for(i=0;i  <2*CHM;i++)//因为有可能被减数与减数的前几位是相等的
        if(result[i]==0) {num3.Exponenet-=2;continue;}
       else
    break;
 
   if(i>  2*CHM-1) {vect.ClrFP(num3);return;}//如果结果等于零,返回(这一步可以在函数开始处进行判断)
          
         if(result[i]>  9) vect.Equ(num3.Mantissa,result+i,CHM);
   else
    {
  //  vect.BCDLeftShift(result+i+1,num3.Mantissa,CHM-1);//???????????
   // num3.Mantissa[0]+=result[i]*10;
    num3.Exponenet--;
              
      vect.BCDRightShift(result+i,CHM+1);
   vect.Equ(num3.Mantissa,result+i,CHM);
    } 
  }
  delete []result;
};

 void FPMul(FP num1,FP num2,FP& num3)
 {
  //首先应文盲判断是否为零
 num3.Exponenet=num1.Exponenet+num2.Exponenet;//指数相加
 num3.Sign=num1.Sign^num2.Sign;//符号异或
 //尾数移位相加
 int i,j;
 short Rnum1[10][CHM+1];//一张乘法表:Rnum1[i][0]:进位标志
 for(i=1;i  <10;i++) Rnum1[i][0]=0;
 for(i=0;i  <=CHM;i++) Rnum1[0][i]=0;
 AddSub8 add;//8位的BCD加法器
 Vector vect;
     for(i=1;i  <=9;i++)//计算被乘数的1-9倍,以便于查表计算结果
  {
  add.CLC();
  for(j=CHM-1;j>  =0;j--)
   Rnum1[i][j+1]=add.ADC8(num1.Mantissa[j],Rnum1[i-1][j+1]);
     
  Rnum1[i][0]=add.ADC8(0,Rnum1[i-1][0]);
 
  }

//为了减少移位,我们分为二个结果,只需移一次即可:移8位运算(0,2,4,6,8;1,3,5,7,9)

short Result1[2*CHM],Result2[2*CHM];//这里定义的数过于大
short Address[2*CHM];
vect.BCD8ToBCD4(num2.Mantissa,CHM,Address);//为了便于查表(可能在机器上实现起来更节约时间):
vect.ClrShort(Result1,2*CHM);
vect.ClrShort(Result2,2*CHM);

    for(i=CHM-1;i>  =0;i--)
    {
  add.CLC();
  if(Address[i*2]!=0)
  for(j=CHM;j>  =0;j--)
  {
     Result1[j+i]=add.ADC8(Result1[j+i],Rnum1[Address[i*2]][j]);   
  }
  
    add.CLC();
  if(Address[i*2+1]!=0)
  for(j=CHM;j>  =0;j--)
  {
     Result2[j+i]=add.ADC8(Result2[j+i],Rnum1[Address[i*2+1]][j]);   
  }
   
 }
//将Result2右移一位,然后两数相加
short Result3[2*CHM+1];
vect.ClrShort(Result3,2*CHM+1);
vect.BCDLeftShift(Result2,Result3,2*CHM);
 
  add.CLC();
  for(i=2*CHM-1;i>  =0;i--)
    Result2[i]=add.ADC8(Result1[i],Result3[i]);
 

    if(Result2[0]==0) vect.Equ(num3.Mantissa,Result2+1,CHM);//进行归一化时可以编一个子程序
    else
 {
        //  vect.BCDLeftShift(Result2+1,num3.Mantissa,CHM-1);//???????????:倒不如一次左移来得干净
   // num3.Mantissa[0]+=Result2[0]*10;
    num3.Exponenet++;

    vect.BCDRightShift(Result2,CHM+1);
    vect.Equ(num3.Mantissa,Result2,CHM);
 }
 }

 bool FPDiv(FP num1,FP num2,FP & num3)
 {
 /* 除法运算:符号异或,阶数相减:尾数
  (1.先检查除数的所谓的有效长度;
   2.两数相减,判断进位标志
   3.保存商
  )采取的运算法则是基于人工的算法,精度可以任意长,我们作了一张除数的倍数表*/
 
 num3.Exponenet=num1.Exponenet-num2.Exponenet;
 num3.Sign=num1.Sign^num2.Sign;

 short L=CHM;//表示除数的长度:
 int i,j,k;
 for(i=CHM-1;i>  =0;i--)
  if(num2.Mantissa[i]==0) {L--;continue;}
  else
  break;
  if(L==0) {num3.Exponenet=120;return false;}//除数为零

short *Dividend,*Divisor[10];//被除数,除数
Dividend=new short[L+1];
for(i=0;i  <10;i++) Divisor[i]=new short[L+1];
Vector vect;
for(i=0;i  <L+1;i++) Divisor[0][i]=0;
AddSub8 add;//8位的BCD加法器

     for(i=1;i  <=9;i++)//计算除数的1-9倍,以便于查表计算结果
  {
  add.CLC();
  for(j=L-1;j>  =0;j--)
   Divisor[i][j+1]=add.ADC8(num2.Mantissa[j],Divisor[i-1][j+1]);
     
  Divisor[i][0]=add.ADC8( ......

>>返回讨论的主题



  发表回复
用户名   *您没有注册?
密码   *
验证码   * .
标题   *
心情
随便说说    我回答你    最新发现    得意的笑   
气死我了    真是没劲    坚决同意    表示反对   
大家过来    好奇怪哟    懒得理它    大家小心   
文件上传
内容


字体:      字体大小:    颜色:
粗体 斜体 下划线 居中 超级连接 Email连接 图片 Flash图片 Shockwave文件 realplay视频文件 Media Player视频文件 QuickTime视频文件 引用 飞行字 移动字 发光字 阴影字 查看更多的心情图标 背景音乐
点击加入表情
                         
选项
有回复时用短消息通知您?

   




老古网执行:63毫秒 最大:78531毫秒 查询6次