高精度算法
在编程进行数值运算时,有时会遇到运算的精度要求特别高,计算的位数达到几十位甚至几百位,虽然计算机的计算精度也算较高了,但因受到硬件的限制,往往达不到实际问题所要求的精度。这种情况下,就需要进行“高精度运算”。
高精度运算首先要处理好数据的接收和存储问题,其次要处理好运算过程中的“进位”和“借位”的问题。
高精度计算中需要处理好,以下几个问题:
-
(1)数据的接收方法和存储方法:
当输入的数很长时,可采用字符串方式输入,这样可输入位数很长的数,利用字符串函数和操作运算,将每一位数取出,存人数组中。void init(int a[]) //传入一个数组
{
string s; //读入字符串s
cin>>s;
a[0]=s.length(); //用 a[0]计算字符串s的位数
for(int i=1;i<=a[0];i++)
a[i]=s[a[0]-i]-'0'; //将数串s转换为数组a,并倒序存储
} -
(2)高精度数位数的确定:
接收时往往是用字符串的,所以它的位数就等于字符串的长度。: -
(3)进位,借位处理
加法进位 : c[i] = a[i] + b[i];
if(c[i]>=10) { c[i]%=10; c[i+1]++; }
减法借位:if (a[i]<b[i]) { a[i+1]--; a[i]+=10; }
c[i] = a[i] - b[i];
乘法进位:c[i+j-1]=a[i]*b[j]+x+c[i+j-1];
x=c[i+j-1]/10;
c[i+j-1]%=10;
- (4)商和余数的求法
商和余数处理: 视被除数和除数的位数情况进行处理。
案例:
1.高精度加法
输入两个1000位以内的正整数,输出它们的和。
【输入样例】
123456789
987654321
【输出样例】
1111111110
【分析】用字符串的方式读入两个高精度数,转存到两个整型数组a和b中,以下模拟加法的过程:从低位(第0位)开始对应为a[i]和b[i]相加,同时处理进位,结果存储
到另一个数组c中。最后,从高位到低位输出c[i]。
7 5 1 2 9 9 6 0 1 9
+ 1 2 3 4 5 6
进位 0 0 0 1 1 0 0 0 1 0
—————————————
7 5 1 3 1 1 9 4 7 5
程序如下:
#include<iostream>
#include<cstring>
using namespace std;
char sa[1010],sb[1010];
int n1,n2,n3,a[1010],b[1010],c[1010];
int main(){
cin>>sa>>sb;
n1=strlen(sa);
n2=strlen(sb);
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
for(int i=0;i<n1;i++)
a[n1-i-1]=sa[i]-'0'; //读入的字符串转成数字存储
for(int i=0;i<n2;i++)
b[n2-i-1]=sb[i]-'0';
n3=n1>n2 ? n1:n2;
memset(c,0,sizeof(c));
for(int i=0;i<n3;i++){
c[i]=a[i]+b[i]+c[i]; //对应为相加,包括上一次运算产生的进位
if(c[i]>=10){ //处理进位
c[i+1]=1;
c[i]-=10;
}
}
if(c[n3]>0) n3++;
for(int i=n3-1;i>=0;i--)
cout<<c[i];
return 0;
}
2.高精度乘法
输入两个1000位以内的正整数,输出它们的乘积。
【输入样例】
123456789
987654321
【输出样例】
121932631112635269
【分析】如图示,模拟“竖式”乘法的过程,用一个数的每一位a[i](从低位开始)逐位与另一位数的每一位b[j]相乘,结果存储到c[i+1]位,同时处理好位数。
9 9 8 1 4
x 3 2 6
————————————
5 9 8 8 8 4
1 9 9 6 2 8
2 9 9 4 4 2
————————————
3 2 5 3 9 3 6 4
程序如下:
#include<iostream>
#include<cstring>
using namespace std;
char sa[1010],sb[1010];
int n1,n2,n3,jw,f,w,a[1010],b[1010],c[10200];
int main(){
cin>>sa>>sb;
n1=strlen(sa);
n2=strlen(sb);
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
for(int i=0;i<n1;i++)
a[n1-i-1]=sa[i]-'0';
for(int i=0;i<n2;i++)
b[n2-i-1]=sb[i]-'0';
memset(c,0,sizeof(c));
jw=0;
for(int i=0;i<n1;i++){
for(int j=0;j<n2;j++){
f=a[i]*b[j]; jw=f/10; f%=10; //处理逐位乘,记录乘积和进位
w=i+j; c[w]=c[w]+f; //将a[i]*b[j]结果存到c[i+j]
c[w+1]=c[w+1]+jw+c[w]/10; //处理进位
c[w]%=10;
}
}
n3=n1+n2; //找到最高位的非0位
while(c[n3]==0) n3--;
for(int i=n3;i>=0;i--)
cout<<c[i];
return 0;
}
3.高精度减法
输入两个1000位以内的正整数,输出它们的差值。
【输入样例】
987654321
123456789
【输出样例】
-864197532
#include<iostream>
#include<cstring>
using namespace std;
char sa[1010],sb[1010],sc[1010];
int n1,n2,n3,jw,f,w,a[1010],b[1010],c[1010];
bool flag=false;
int main(){
cin>>sa>>sb;
n1=strlen(sa);
n2=strlen(sb);
if(strcmp(sa,sb)<0){
strcpy(sc,sa);
strcpy(sa,sb);
strcpy(sb,sc);
flag=true;
}
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
for(int i=0;i<n1;i++)
a[n1-i-1]=sa[i]-'0';
for(int i=0;i<n2;i++)
b[n2-i-1]=sb[i]-'0';
n3=n1>n2 ? n1:n2;
memset(c,0,sizeof(c));
for(int i=0;i<n3;i++){
if(a[i]<b[i]) {
c[i]=a[i]+10-b[i];
a[i+1]--;
}else{
c[i]=a[i]-b[i];
}
}
if(flag) cout<<"-";
while(c[n3-1]==0 ) n3--;
for(int i=n3-1;i>=0;i--)
cout<<c[i];
return 0;
}
4.n/m的精确值。
输入n和m,输出n除以m的精确值。假设n和m在int范围以内,结果精确到小数点后100位。
【输入样例】
355 113
【输出样例】
3.1415929203539823008849557522123893805309734513274336283185840707964601769911504424778761061946902654
【分析】模拟数学中的“短除法”。由数学知识可知,除法运算中被除数、除数和商、余数的关系为:
新的被除数 = 10 x 余数
商 = 被除数 / 除数
余数 = 被除数 % 除数
3. 7 5
____________
8√ 3 0
2 4
———
6 0
5 6
———
4 0
4 0
———
0
(高精度除法示意图)
程序如下:
#include<iostream>
using namespace std;
int n,m,b[100],s[100],y[100];
int main(){
cin>>n>>m;
b[0]=n; s[0]=n/m; y[0]=n%m;
cout<<s[0];
if(y[0]!=0) cout<<".";
for(int i=1; i<=100; i++){
if(y[i-1]==0) break;
b[i]=y[i-1]*10;
s[i]=b[i]/m;
cout<<s[i];
y[i]=b[i]%m;
}
cout<<endl;
return 0;
}