【大整数的存储】
-
例如将整数 235813 存储到数组中,则有 d[0] = 3, d[1] = 1, d[2] = 8, d[3] = 5, d[4] = 3, d[5] = 2,即整数的高位存储在数组的高位,整数的低位存储在数组的低位。不反过来存储的原因是,在进行运算的时候都是从整数的低位到高位进行枚举,顺位存储和这种思维相合。
-
但是也会由此产生一个需要注意的问题:把整数按字符串 %s 读入的时候,实际上是逆位存储的,即 str[0] = 2’, str[1] = ‘3’, …, str[5] = ‘3’,因此在读入之后需要在另存为至 d[] 数组的时候反转一下。
-
而为了方便随时获取大整数的长度,一般都会定义一个 int 型变量 len 来记录其长度,并和 d 数组组合成结构体:
struct bign
{
int d[1000];
int len;
bign()
{
memset(d, 0, sizeof(d));
len = 0;
}
};
-
这里 bign 是 big number 的缩写。
-
显然,在定义结构体变量之后,需要马上初始化结构体。为了减少在实际输入代码时总是忘记初始化的问题,最好使用“构造函数” 来初始化结构体的函数,其函数名和结构体名相同、无返回值,这样在每次定义结构体变量时,都会自动对该变量进行初始化。
-
而输入大整数时,一般都是先用字符串读入,然后再把字符串另存为至 bign 结构体。由于使用 char数组进行读入时,整数的高位会变成数组的低位,而整数的低位会变成数组的高位,因此为了让整数在 bign 中是顺位存储,需要让字符串倒着赋给 d[] 数组:
bign change(char str[]) //将整数转换为bign
{
bign a;
a.len = strlen(str); //bign的长度就是字符串的长度
for(int i=0;i<a.len;i++)
a.d[i] = str[a.len-i-1] - '0';//逆着赋值
return a;
}
【大整数的比较】
- 如果要比较两个 bign 变量的大小,规则也很简单:先判断两者的 len 大小,如果不相等,则以长的为大;如果相等,则从高位到低位进行比较,直到出现某一位不等,就可以判断两个数的大小。
下面的代码直接依照了这个规则:
int compare(bign a, bign b)//比较a和b大小,a大、相等、a小分别返回1、0、-1
{
if(a.len>b.len)
return 1;//a大
else if(a.len<b.len)
return -1;//a小
else
{
for(int i=a.len-1;i>=0;i--) //从高位往低位比较
{
if(a.d[i]>b.d[i]) //只要有一位a大,则a大
return 1;
else if(a.d[i]<b.d[i]) //只要有一位a小,则a小
return -1;
}
return 0; //两数相等
}
}
【高精度加法】
bign add(bign a, bign b) //高精度a+b
{
bign c;
int carry = 0; //carry是进位
for(int i=0;i<a.len||i<b.len;i++) //以较长的为界限
{
int temp = a.d[i] + b.d[i] + carry; //两个对应位与进位相加
c.d[c.len++] = temp%10; //个位数为该位结果
carry = temp/10; //十位数为新的进位
}
if(carry!=0) //如果最后进位不为0,则直接赋给结果的最高位
c.d[c.len++] = carry;
return c;
}
//用法样例
void print(bign a)
{
for(int i=a.len-1;i>=0;i--)
printf("%d",a.d[i]);
}
int main()
{
char str1[1000],str2[1000];
scanf("%s%s",str1,str2);
bign a = change(str1);
bign b = change(str2);
print(add(a,b));
return 0;
}
- 最后指出,这样写法的条件是两个对象都是非负整数。如果有一方是负的,可以在转换到数组这步时去掉其负号,然后采用高精度减法;如果两个都是负的,就都去掉负号后用高精度加法,最后再把负号加回去即可。
【高精度减法】
bign sub(bign a, bign b) //高精度a-b
{
bign c;
for(int i=0;i<a.len||i<b.len;i++) //以较长的为界限
{
if(a.d[i]<b.d[i]) //如果不够减
{
a.d[i+1]--; //向高位借位
a.d[i] += 10; //当前位加10
}
c.d[c.len++] = a.d[i] - b.d[i]; //减法结果为当前位结果
}
while(c.len-1>=1&&c.d[c.len-1]==0)
c.len--; //去除高位的0,同时至少保留一位最低位
return c;
}
【高精度乘低精度】
bign multi(bign a, bign b) //高精度乘低精度
{
bign c;
int carry = 0;//进位
for(int i=0;i<a.len;i++) //以较长的为界限
{
int temp = a.d[i]*b+carry;
c.d[c.len++] = temp%10; //个位作为该位结果
carry = temp/10; //高位部分作为新的进位
}
while(carry!=0) //和加法不一样,乘法进位可能不止一位,因此用while
{
c.d[c.len++] = carry%10;
carry /= 10;
}
return c;
}
- 如果a和b中存在负数,需记录下其负号,然后取它们的绝对值代入函数
【高精度除低精度】
bign divide(bign a, bign b,int &r) //高精度除低精度,r为余数
{
bign c;
c.len = a.len; //被除数的每一位和商的每一位是一一对应的,因此先令长度相等
for(int i=a.len-1;i>=0;i--) //从高位开始
{
r = r*10 + a.d[i]; //和上一位遗留的余数组合
if(r<b)
c.d[i] = 0; //不够除,该位为0
else
{
c.d[i] = r/b; //商
r = r%b; //获得新的余数
}
}
while(c.len-1>=1&&c.d[c.len-1]==0)
c.len--; //去除高位的0,同时至少保留一位最低位
return c;
}