先来看一下每个数据类型可表示的数据范围
当我们要表示的数很长时,无法用数据类型表示,可以用数组存储
单精度:能用一个内置类型存储的整数
高精度:不能用内置类型存储的大整数,通常用数组存储每一个数位
建议使用小端存储(个位放在最前面)(原因:大端存储虽然看似更直观,但是当处理进位时就会遇到问题:必须要将所有元素移位,为新位腾出空间!方便模拟竖式运算!)
高精度加法
按照竖式计算方法,①从低位到高位,②对于较长数的每一位i,和的第i位为两加数第i位与当前进位三者之和,③若和大于10,则下一进位为1,当前位和减10,④结束后,若进位非0,则结果增加一位,值为进位(此处必为1)
// 从低到高位计算
for (int i = 0; i < len; i++) {
// 和的第i位为两加数第i位与当前进位三者之和
c[i] += a[i] + b[i];
// 若和大于10,则下一进位为1
c[i + 1] = c[i] / 10;
// 当前位和减10
c[i] %= 10;
}
// 结束后,若进位非0,则结果增加一位,值为进位(此处必为1)
if (c[len + 1])len++;
完整程序:
#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 520
using namespace std;
int a[maxn], b[maxn], c[maxn]
int main()
{
string A,B;
cin>>A>>B; //输入加数与被加数
int len=max(A.length(),B.length());//和的位数与最大值的位数有关
for (int i=A.length()-1,j=1; i>=0;i--,j++)
a[j]=A[i]-'0'; //加数放入a数组
for (int i=B.length()-1,j=1; i>=0;i--,j++)
b[j]=B[i]-'0';
for (int i=1; i<=len;i++){
c[i]+=a[i]+b[i];
c[i+1]=c[i]/10;
c[i]%=10;
}
if(c[len+1])len++;
for(int i=len;i>=1;i--)cout<<c[i];
return 0;
使用数组0~(n-1)和使用数组1~n在进行高精度运算时,分别有什么好处?
高精度乘法
算法分析
类似加法,可以用竖式求乘法。
分析c数组下标的变化规律,可以写出如下关系式:ci = c’i +c”i +…由此可见,c i跟a[i]*b[j]
乘积有关,跟上次的进位有关,还跟原c 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;
完整程序如下:
#include<iostream>
#include<cstring>
#define maxn 5010
using namespace std;
int a[maxn],b[maxn],c[maxn];
int main()
{
int i,j,x,lena,lenb,lenc;
string A,B;
cin>>A>>B;
lena=A.length();lenb=B.length();
for (i=lena-1;i>=0;i--) a[lena-i]=A[i]-48;
for (i=lenb-1;i>=0;i--) b[lenb-i]=B[i]-48;
for (i=1;i<=lena;i++)
for (j=1;j<=lenb;j++)
c[i+j-1]+=a[i]*b[j];
lenc=lena+lenb;
for(i=1;i<=lenc;i++){
c[i+1]+=c[i]/10;
c[i]%=10;
}
for(;!c[lenc];)lenc--;
for (i=max(lenc,1);i>=1;i--)
cout<<c[i];
return 0;
}
补充:高精度减法
算法分析
类似加法,可以用竖式求减法。在做减法运算时,需要注意的是:被减数必须比减数大,同时需要处理借位。
减法进位
if (a[i]<b[i]) {
--a[i+1];
a[i]+=10;
}
c[i]=a[i]-b[i];
完整程序如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 520
using namespace std;
int a[maxn], b[maxn], c[maxn];
int main()
{
int i,j,lena,lenb,lenc;
char A[maxn],B[maxn],C[maxn];
gets(A);
gets(B);
if (strlen(A)<strlen(B)||strlen(A)==strlen(B)&&strcmp(A,B)<0)//strcmp()为字符串比较函数,当n1==n2, 返回0,A>B时,返回正整数;A<B时,返回负整数
{//处理被减数和减数,交换被减数和减数
strcpy(C,A);
strcpy(A,B);
strcpy(B,C);
cout<<"-";
}
lena=strlen(A),lenb=strlen(B);
for(i=lena-1,j=1;i>=0;i--,j++)
a[j]=A[i]-'0'; //加数放入a数组
for(i=lenb-1,j=1;i>=0;i--,j++){
b[j]=B[i]-'0';
}
i=1;
while (i<=lena||i<=lenb)
{
if (a[i]<b[i])
{
a[i]+=10;//不够减,那么向高位借1当10
a[i+1]--;
}
c[i]=a[i]-b[i];//对应位相减
i++;
}
lenc=i;
while ((c[lenc]==0)&&(lenc>1)) lenc--;//最高位的0不输出
for (i=lenc;i>=1;i--) cout<<c[i];//输出结果
return 0;
}
高精度除法
1. 高精度数除以单精度数
算法分析
做除法时,每一次上商的值都在0~9,每次求得的余数连接以后的若干位得到新的被除数,继续做除法。
因此,在做高精度除法时,要涉及到乘法运算和减法运算,还有移位处理。
当然,为了程序简洁,可以避免高精度除法,用0~9次循环减法取代得到商的值。这里,我们讨论一下高精度数除以单精度数的结果,采取的方法是按位相除法。
完整程序如下:
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int main()
{
char a1[100],c1[100];
int a[100],c[100],lena,i,x=0,lenc,b;
memset(a,0,sizeof(a));
memset(c,0,sizeof(c));
gets(a1);
cin>>b;
lena=strlen(a1);
for (i=0;i<=lena-1;i++)
a[i+1]=a1[i]-48;
for (i=1;i<=lena;i++) //按位相除
{
c[i]=(x*10+a[i])/b;
x=(x*10+a[i])%b;
}
lenc=1;
while (c[lenc]==0&&lenc<lena)
lenc++; //删除前导0
for (i=lenc;i<=lena;i++)
cout<<c[i];
cout<<endl;
return 0;
}
实质上,在做两个高精度数运算时候,存储高精度数的数组元素可以不仅仅只保留一个数字,而采取保留多位数(例如一个整型或长整型数据等),这样,在做运算(特别是乘法运算)时,可以减少很多操作次数。
例如图5就是采用4位保存的除法运算,其他运算也类似。具体程序可以修改上述例题予以解决,程序请读者完成。
2.高精除以高精
算法分析
高精除以低精是对被除数的每一位(这里的“一位”包含前面的余数,以下都是如此)都除以除数,而高精除以高精则是用减法模拟除法,对被除数的每一位都减去除数,一直减到当前位置的数字(包含前面的余数)小于除数(由于每一位的数字小于10,所以对于每一位最多进行10次计算)
完整程序如下:
#include<iostream>
#include<cstring>
using namespace std;
int a[101],b[101],c[101],d,i;
void init(int a[])
{ string s;
cin>>s; //读入字符串s
a[0]=s.length(); //用a[0]计算字符串 s的位数
for(i=1;i<=a[0];i++)
a[i]=s[a[0]-i]-'0'; //将数串s转换为数组a,并倒序存储.
}
void print(int a[]) //打印输出
{
if (a[0]==0){cout<<0<<endl;return;}
for(int i=a[0];i>0;i--) cout<<a[i];
cout<<endl;
return ;
}
int compare (int a[],int b[])
//比较a和b的大小关系,若a>b则为1,a<b则为-1,a=b则为0
{
if(a[0]>b[0]) return 1; //a的位数大于b则a比b大
if(a[0]<b[0]) return -1; //a的位数小于b则a比b小
for(i=a[0];i>0;i--) //从高位到低位比较
{
if (a[i]>b[i]) return 1;
if (a[i]<b[i]) return -1;
}
return 0; //各位都相等则两数相等。
}
void numcpy(int p[],int q[],int det) //复制p数组到q数组从det开始的地方
{
for (int i=1;i<=p[0];i++) q[i+det-1]=p[i];
q[0]=p[0]+det-1;
}
void jian(int a[],int b[]) //计算a=a-b
{
int flag,i;
flag=compare(a,b); //调用比较函数判断大小
if (flag==0) {a[0]=0;return;} //相等
if(flag==1) //大于
{
for(i=1;i<=a[0];i++)
{
if(a[i]<b[i]){ a[i+1]--;a[i]+=10;} //若不够减则向上借一位
a[i]-=b[i];
}
while(a[0]>0&&a[a[0]]==0) a[0]--; //修正a的位数
return;
}
}
void chugao(int a[],int b[],int c[])
{
int tmp[101];
c[0]=a[0]-b[0]+1;
for (int i=c[0];i>0;i--)
{
memset(tmp,0,sizeof(tmp)); //数组清零
numcpy(b,tmp,i);
while(compare(a,tmp)>=0){c[i]++;jian(a,tmp);} //用减法来模拟
}
while(c[0]>0&&c[c[0]]==0)c[0]--;
return ;
}
int main()
{
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
memset(c,0,sizeof(c));
init(a);init(b);
chugao(a,b,c);
print(c);
print(a);
return 0;
}