一.引言
对于数字的储存,用实数类型总会有一些不足,比如:
使用int 只能最多存储4个字节,范围也就是2的32次方;
使用double 只能最多储存8字节,就是2的64次方;
如果数字超过这个限制,就会发生溢出,导致结果错误。那么我们该如何解决呢?
为了解决这个问题,我们就要回归到我们小学所学的知识 :
列式计算
二.一般步骤:
1.高精度加法:
a.将字符组转化为数组(注意顺序问题,影响到后续计算)
b.计算是否进位,如果进位则下一个结果项加上进位数
c.将两个数组各项以及进位相加,得到结果的数组
d.按顺序输出(顺序取决于第一步的顺序)
PS:减法只是将进位换位借位,大体不变
2.高精度减法:
a.提前开一个数组,以存放未除尽的的的数
b.将字符组转化为数组(注意顺序问题,影响到后续计算)
c.用除法结果作为这一位的结果,用求余来放到开的数组里
d.按顺序输出(顺序取决于第一步的顺序)
PS:这个说的有点抽象,具体可参考第二个例题
3.高精度乘法乘法:
a.提前开一个数组,以存乘积后的的的数
b.将字符组转化为数组(注意顺序问题,影响到后续计算)
c.需要注意乘法的数组长等于两个相乘数组长度之和
c.乘法结果作为这一位的结果,放到开的数组里
e.如果大于10,对10的余数加到下一位,这一位留下除以10的值
d.按顺序输出(顺序取决于第一步的顺序)
三.例题
1.高精度加法:
首先看一个代码,这是早期人类初步接触高精度加法时的 “ 作品 ” :

#include<bits/stdc++.h>
using namespace std;
int main()
{
char a[100],b[100];
//用于记录2个高精度数的每一位
char aa[100],bb[100];
//后期用于储存没有前导零的字符组
for(int i=0;i<100;i++)
{
aa[i]=bb[i]='0';
}
int m=0,n=0;
int sum=0,sum_=0;
//标记2个字符组的位数
char x;
while((x=getchar())!='\n')
{
a[m]=x;
m++;
}
while((x=getchar())!='\n')
{
b[n]=x;
n++;
}
//输入两字符组
int a_=0,b_=0;
int c_=max(m,n);
//a_,b_代表处理过前导零后的位数
//c_代表两者字符组所含更大的位数
for(int i=0;i<c_;i++)
{
if(a_==0&&a[i]=='0')
continue;
else if(i>=m)
continue;
else
{
aa[a_]=a[i];
sum_++;
a_++;
}
}
reverse(aa,aa+a_);
for(int i=0;i<c_;i++)
{
if(b_==0&&b[i]=='0')
continue;
else if(i>=n)
continue;
else
{
bb[b_]=b[i];
sum++;
b_++;
}
}
//去除前导零,比如:008->8
reverse(bb,bb+b_);
//反转操作,将顺序调整
int ma=max(sum,sum_);
char k[101];
int st=0;
//st代表进位
for(int i=0;i<ma;i++)
{
int q=aa[i]-'0'+bb[i]-'0';
k[i]=char((q+st)%10+'0');
st=q/10;
}
if(st!=0) cout<<char(st+'0');
for(int i=ma-1;i>=0;i--)
{
cout<<k[i];
}
cout<<endl;
return 0;
}
这个代码的问题有以下几点:
1.处理前导零的时间过早,导致步骤冗杂
2.没有第一时间将字符组转化为数组
3.对字符组中字符的顺序没有处理好,导致顺序错乱,使代码凌乱
2.高精度加,乘,除
再看一个比较简洁的代码,这个已经解决了如上的问题,并按照正常顺序书写:

首先用等差数列求和计算出 n*(n+1)/2
所以,先计算n+1:
for(int i=0;i<len;i++)
{
a[len-1-i]=s1[i]-'0';
}
//直接进行了换位操作
b[0]=a[0]+1;
//这个地方不要处理余数 ,会影响后面求除法那步
for(int i=1;i<=len;i++)
{
b[i]=a[i]+b[i-1]/10;
}
for(int i=0;i<len;i++)
{
b[i]%=10;
}
if(b[len]>0) len++;
然后,计算n*(n+):
for(int i=0;i<s1.length();i++)
{
for(int j=0;j<len;j++)
{
int k=i+j;
//注意处理乘积的位数
c[k]+=a[i]*b[j];
//切记谁对应i,谁对应j
if(c[k]>=10)
{
c[k+1]+=c[k]/10;
c[k]%=10;
}
}
}
len=s1.length()+len+1;
if(c[len]>0) len++;
while(c[len-1]==0&&len>1) len--;
//去首位的0
一定要注意谁对应 i,谁对应 j ,笔者因为这个问题调试了半个小时。
最后,计算除2的值:
int t=0;
for(int i=len-1;i>=0;i--)
{
d[i]=(c[i]+t)/2;
t=c[i]%2*10;
}
while(d[len-1]==0&&len>1) len--;
//去除前导零
for(int i=len-1;i>=0;i--)
{
cout<<d[i];
}
拼接起来组成完整代码:
#include<iostream>
#include<string.h>
using namespace std;
int main()
{
string s1;
int a[105]={0},b[105]={0},c[205]={0},d[205]={0};
cin>>s1;
int len=s1.length();
for(int i=0;i<len;i++)
{
a[len-1-i]=s1[i]-'0';
}
//直接进行了换位操作
b[0]=a[0]+1;
//这个地方不要处理余数 ,会影响后面求除法那步
for(int i=1;i<len;i++)
{
b[i]=a[i]+b[i-1]/10;
}
for(int i=0;i<len;i++)
{
b[i]%=10;
}
if(b[len]>0) len++;
//注意部位
for(int i=0;i<s1.length();i++)
{
for(int j=0;j<len;j++)
{
int k=i+j;
//注意处理乘积的位数
c[k]+=a[i]*b[j];
//切记谁对应i,谁对应j
if(c[k]>=10)
{
c[k+1]+=c[k]/10;
c[k]%=10;
}
}
}
len=s1.length()+len+1;
if(c[len]>0) len++;
while(c[len-1]==0&&len>1) len--;
//去首位的0
int t=0;
for(int i=len-1;i>=0;i--)
{
d[i]=(c[i]+t)/2;
t=c[i]%2*10;
}
while(d[len-1]==0&&len>1) len--;
for(int i=len-1;i>=0;i--)
{
cout<<d[i];
}
return 0;
}
其实,高精度唯一需要注意的两点就是:
1.注意进位和借位
2.注意字符组中的顺序
注意到这些,再多加练习,就可以想写低精度加法一样顺利了。
以上就是今天分享的内容了,希望对有需求的码友有帮助。
693

被折叠的 条评论
为什么被折叠?



