高精度学习笔记
c语言中任意数据类型都是有范围的,当一个数的范围超过了任意一个范围,那么这个数就被称为高精度数。
输入与储存
通常是用字符串来储存高精度数,每一个元素对应一位。例如:99100,数组储存便是
a[1]=0;
a[2]=0;
a[3]=1;
…
注意:因为我们用字符串来储存高精度数,所以数的低位也要对应字符数组的低位。
另外我们也要将高精度数的位数储存在a[0]的位置。那么上面的例子中a[0]=5;
a[0] | a[1] | a[2] | a[3] | a[4] | a[5] |
---|---|---|---|---|---|
5 | 0 | 0 | 1 | 9 | 9 |
代码实现如下:
#include<cstdio>
#include<cstring>
#define MAXLEN 240//最大的长度为240
int a[MAXLEN+10];
char s[MAXLEN+10];
int main()
{
scanf("%s",s);
a[0]=strlen(s);
for(int i=0;i<a[0];i++)
a[a[0]-i]=s[i]-'0';
for(int i=a[0];i>0;i--)
printf("%d",a[i]);
}
加法
假定两个数a,b相加,在高精度中我们依次将它们的相应的位加到一个c数组中。然后进行进位处理,如果这位的数超过了10那么就向它的上一位进1。最后将高位的0去掉。即:
- 相加到另一个数组
- 进位
- 去掉高位的0
代码实现如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXLEN 240
using namespace std;
int a[MAXLEN+10],b[MAXLEN+10],c[MAXLEN+10];
char s[MAXLEN+10];
int main()
{
scanf("%s",s);
a[0]=strlen(s);
for(int i=0;i<a[0];i++)
a[a[0]-i]=s[i]-'0';
memset(s,0,sizeof(s));
scanf("%s",s);
b[0]=strlen(s);
for(int i=0;i<b[0];i++)
b[b[0]-i]=s[i]-'0';
c[0]=max(a[0],b[0]);
for(int i=1;i<=c[0];i++)
c[i]=a[i]+b[i];
c[0]++;//加法最多进一位
for(int i=1;i<=c[0];i++)
{
c[i+1]+=c[i]/10;
c[i]%=10;
}
while(c[0]>1&&c[c[0]]==0)
c[0]--;
for(int i=c[0];i>0;i--)
printf("%d",c[i]);
}
减法
依照由低位至高位的顺序进行减法运算。在每一次位运算中,若出现不够减的情况,则向高位借位。在进行了减运算后,若高位为0,则要减少结果的长度。
- 进行减法
- 处理借位
- 去除高位的0
代码如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXLEN 240
using namespace std;
int a[MAXLEN+10],b[MAXLEN+10],c[MAXLEN+10];
char s[MAXLEN+10];
int main()
{
bool f=0;
scanf("%s",s);
a[0]=strlen(s);
for(int i=0;i<a[0];i++)
a[a[0]-i]=s[i]-'0';
memset(s,0,sizeof(s));
scanf("%s",s);
b[0]=strlen(s);
for(int i=0;i<b[0];i++)
b[b[0]-i]=s[i]-'0';
int len=max(a[0],b[0]);
if(memcmp(a,b,len)<0)
{
memcpy(c,a,(len+1)*4);
memcpy(a,b,(len+1)*4);
memcpy(b,c,(len+1)*4);
f=1;
}
memset(c,0,sizeof(c));
c[0]=len;
for(int i=1;i<=c[0];i++)
c[i]=a[i]-b[i];
for(int i=1;i<=c[0];i++)
if(c[i]<0)
{
c[i]+=10;
c[i+1]-=1;
}
while(c[0]>1&&c[c[0]]==0)
c[0]--;
if(f)
printf("-");
for(int i=c[0];i>0;i--)
printf("%d",c[i]);
}
乘法
首先我们要估计所需要的数组长度,然后被乘数从低位向高位逐位乘以乘数。
- 逐位从低位向高位乘(较短的数每一个数都要乘以较长数的每一位)
- 处理进位(因为我们并不知道题中数有多少为,所以在普通处理后要用一个while继续处理)
- 去除高位的0
代码如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXLEN 500
using namespace std;
int a[MAXLEN+10],b[MAXLEN+10],c[MAXLEN*2+10];
char s[MAXLEN+10];
int main()
{
scanf("%s",s);
a[0]=strlen(s);
for(int i=0;i<a[0];i++)
a[a[0]-i]=s[i]-'0';
memset(s,0,sizeof(s));
scanf("%s",s);
b[0]=strlen(s);
for(int i=0;i<b[0];i++)
b[b[0]-i]=s[i]-'0';
c[0]=max(a[0],b[0]);
if(memcmp(a,b,c[0])<0)//这里要让a数组是较长的数,b数组是较短的数。
{
memcpy(c,a,sizeof(a));
memcpy(a,b,sizeof(b));
memcpy(b,c,sizeof(c));
}
memset(c,0,sizeof(c));
c[0]=max(a[0],b[0]);
for(int i=1;i<=b[0];i++)
for(int j=1;j<=a[0];j++)
c[i+j-1]+=a[j]*b[i];//这里按照竖式计算的格式,所以i+j-1。
for(int i=1;i<=c[0];i++)
{
c[i+1]+=c[i]/10;
c[i]%=10;
}
c[0]++;
while(c[c[0]]>=10)
{
c[c[0]+1]+=c[c[0]]/10;
c[c[0]]%=10;
c[0]++;
}
while(c[0]>1&&c[c[0]]==0)
c[0]--;
for(int i=c[0];i>0;i--)
printf("%d",c[i]);
}
除法
基本方法是从被除数的最高位开始,逐位计算商和余数。存储商。余数则转移到下一次的被除数中。
注意在下一次的计算中,余数要乘以10,然后再加上当前位的被除数。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXLEN 500
using namespace std;
int a[MAXLEN+10],b,c[MAXLEN*2+10];
int r;//余数
char s[MAXLEN+10];
int main()
{
scanf("%s",s);
a[0]=strlen(s);
for(int i=0;i<a[0];i++)
a[a[0]-i]=s[i]-'0';
memset(s,0,sizeof(s));
scanf("%d",&b);
memset(c,0,sizeof(c));
for(int i=a[0];i>0;i--)
{
r=r*10+a[i];
c[i]+=r/b;
r=r%b;
}
while(a[0]>1&&c[a[0]]==0)
a[0]--;
c[0]=a[0];
for(int i=c[0];i>0;i--)
printf("%d",c[i]);
printf("\n%d",r);
}