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( ......
>>返回讨论的主题
|