目录
高精度计算是模拟算法的一种,通过字符串和整型数组的使用,将加减乘除的竖式运算模拟出来,达到计算的目的。其步骤大致分为:一,将字符串数据存到整型数组中,二,模拟算法,不同的算式对准不同的模拟代码,三,将保存的数据转移。
高精度加法
void addhigh(char a[],char b[],char res[])//a,b分别为两个加数的字符串,res返回答案
{
int a_num[2005],b_num[2005]; //保存字符串a,b的数组
for(int i=0;i<2005;i++) //初始化,保证开始每位上数字为0
a_num[i]=0,b_num[2005]=0;
//因为在竖式运算中我们是从低位算起,所以我们倒序将a,b存到数组里
for(int i=strlen(a)-1,j=0;i>=0;i--,j++) //倒序将a保存为数组,方便每一位的模拟
a_num[j]=a[i]-'0';
for(int i=strlen(b)-1,j=0;i>=0;i--,j++) //倒序将b保存为数组,方便每一位的模拟
b_num[j]=b[i]-'0';
int lenth_max = strlen(a)>strlen(b)?strlen(a):strlen(b); //计算长度,方便记录位数
for(int i=0;i<lenth_max;i++) //加法运算的核心,其他(乘法减法)都是这里来实现
{
a_num[i]+=b_num[i]; //a的每一位加上b的每一位;
if(a_num[i]>=10) //因为是十进制,
a_num[i+1]+=a_num[i]/10; //下一位加上该为的10的除数,模拟进位
a_num[i]=a_num[i]%10; //大于十我们需要取10的模数,
}//提示,如果是其他进制,只需要将10变为其他数
if(a_num[lenth_max]!=0) //因为加法最多只能进一位
lenth_max++;
for(int i=lenth_max-1,j=0;i>=0;i--,j++)//将结果转移到字符串中
res[j]=a_num[i]+'0';
res[lenth_max]='\0';//将结果后打上终止符方便输出
}
观察代码发现实际上实现高精度加法的核心在于中间的一个for循环,其他全都是形同的预处理,所以我们可以想到高精度乘法的处理方式
高精度乘法
void multihigh(char a[],char b[],char res[])
{
int lenth_a=strlen(a), lenth_b=strlen(b);
int a_num[2005],b_num[2005],res_num[2005];
for(int i=0;i<2005;i++)
a_num[i]=b_num[i]=res_num[i]=0;
for(int i=1;i<=lenth_a;i++) a_num[i]=a[lenth_a-i]-'0';
for(int i=1;i<=lenth_b;i++) b_num[i]=b[lenth_b-i]-'0';
//-----------------------------------------------核心代码
for(int i=1;i<=lenth_a;i++)//通过两重的循环实现每次数乘玩之后都要乘10
for(int j=1;j<=lenth_b;j++)
res_num[i+j-1]+=a_num[i]*b_num[j];//a,b的每一位相乘
for(int i=1;i<lenth_a+lenth_b;i++)//十进制处理,大于10模除进位
if(res_num[i]>=10){
res_num[i+1]+=res_num[i]/10;
res_num[i]%=10;
}
int lenth_max=lenth_a+lenth_b;//两数相乘后,结果的位数不会比两加数的位数之和大,方便后面找位数时降低循环次数
while(res_num[lenth_max]==0&&lenth_max>1) lenth_max--;//从后找第一个非0的元素即为最高位数,从前找0不行;
//-------------------------------------------------核心代码
for(int i=lenth_max,j=0;i>0;i--,j++) res[j]=res_num[i]+'0';
res[lenth_max]='\0';
}
乘法和加法基本一样,但是高精度减法却又多了正负的判断。
高精度减法
首先写a字符串>b字符串的减法函数,这样如果a<b时只需将a,b位置颠倒;
void minHighlong(char a[],char b[],char res[])
{
int a_num[2005],b_num[2005];
int lenth_max=strlen(a);
for(int i=0;i<2005;i++)
a_num[i]=0,b_num[2005]=0;
for(int i=strlen(a)-1,j=0;i>=0;i--,j++)
a_num[j]=a[i]-'0';
for(int i=strlen(b)-1,j=0;i>=0;i--,j++)
b_num[j]=b[i]-'0';
//-----------------------------------------核心代码
for(int i=0;i<strlen(a);i++)
{
a_num[i]-=b_num[i]; //a的数组减去b的数组
if(a_num[i]<0) a_num[i]+=10,a_num[i+1]--;
//如果a的该位数小于0,将取前一位数的10来填补,同时前一位减去1
}
//-----------------------------------------核心代码
while(a_num[lenth_max]==0&&lenth_max>0) lenth_max--;
for(int i=lenth_max,j=0;i>=0;i--,j++)
res[j]=a_num[i]+'0';
res[lenth_max+1]='\0';
}
接下来我们只需要写个函数实现判断a和b的大小关系,代码如下
int compare(char a[],char b[])
{
if(strlen(a)<strlen(b))//比较长度,a<b的话返回0;
return 0;
if(strlen(a)>strlen (b))//同理
return 1;
else{
for(int i=strlen(a);i>=0;i--)//长度相同逐位数去比较,由高向低比较数字大小
if(a[i]<b[i])
return 0;
}
return 1;
}
不能用strcmp(),strcmp() 会根据 ASCII 编码依次比较 str1 和 str2 的每一个字符,直到出现不到的字符,或者到达字符串末尾(遇见\0
)。不符合数学比大小。
最后将这两个函数组合起来就形成了最终函数
void minHigh(char a[],char b[],char res[])
{
if(compare(a,b))//比较后确定ab的位置
minHighlong(a,b,res);
else{
minHighlong(b,a,res);//将结果加工成负数
res[strlen(res)+1]='\0';//加休止符确定末尾
for(int i=strlen(res);i>0;i--) res[i]=res[i-1];//从末尾向后移动一位
res[0]='-';//加上符号
}
}
高精度除法
高精除低精度我们可以模拟,但是高精除高精我们只能利用高精度减法一个一个减去,最后计算出结果,同时我们的结果也可能是高精度,所以在结果的处理上我们需要用高精度加法。
void dividHigh(char a[],char b[],char res[])
{
res[0]='0';
res[1]='\0';
char one[2];
one[0]='1';
one[1]='\0';
while(compare(a,b))//当a<b时说明不能除了,结束
{
addhigh(res,one,res);//结果增加
minHigh(a,b,a);//减去一个除数
}
}
高精度除法用的都是之前的加减函数来实现,这样效率特别低,所以判断条件时能用高精除低精时,尽量用高精除低精,其中高精除低精核心代码如下:
for(i=strlen(a)-1;i>=0;i--){
reminder=reminder*10+a_num[i]; //模拟除法
ans[i]=reminder/b;
reminder%=b;
}
高精度模法
高精度模低精度的话,我们只需读一位(得乘10)模一位就可以得到最终答案,
int mod(string a, int b) {
int d = 0;
for (int i = 0; i < a.size(); i++)
{
d = (d * 10 + (a[i] - '0')) % b;
}
return d;
}
每次模之后可以将数字不断的缩小到精度以内,但是高精度模高精度却不可以,所以我们在处理高精模高精度时,只需将除法改下就可以了,即利用高精循环去减,判断大小即可,这里就不赘述了
高精度运算就在于对每一位运算的模拟,处理好进位,拿一,相乘之后,就可实现整个运算。