大整数又称为高精度整数,其含义就是用基本数据类型无法储存其精度的整数。
一、大整数的存储
很简单,使用数组即可。例如定义 int 型数组 d[1000],那么这个数组的每一位代表了存放整数的每一位。整数的高位储存在数组的高位,整数的低位储存在数组的低位。
而为了方便随时获取大整数的长度,一般都会定义一个 int 型变量 len 来记录其长度,结构体如下:
1 // 大整数 2 struct bign { 3 int d[1000]; 4 int len; 5 };
在定义结构体变量后,需要马上初始化结构体,加上“构造函数”,代码如下:
// 大整数 struct bign { int d[1000]; int len; bign() { // 构造函数 memset(d, 0, sizeof(d)); len = 0; } };
而输入大整数时,一般都是先用字符串读入,然后再把字符串另存为至 bign 结构体。由于使用 char 数组进行读入时,整数的高位会变成数组的低位,因此需要让字符串倒着赋给 d[] 数组:
1 // 将字符串另存至 bign 结构体 2 bign change(char str[]) { 3 bign a; 4 a.len = strlen(str); // bign 的长度就是数组的长度 5 int i; 6 for(i=0; i<a.len; ++i) { // 倒着赋值 7 a.d[i] = str[a.len-1-i] - ‘0‘; 8 } 9 return a; 10 }
如果要比较两个 bign 变量的大小,规则也很简单:先判断两者的 len 的大小,如果不相等,则以长的为大;如果相等,则从高位到低位进行比较,直到出现某一位不等,就可以判断两个数的大小。代码如下:
1 // 比较两个大整数变量的大小 2 // a 大、相等、小分别返回 1、0、-1 3 int compare(bign a, bign b) { 4 if(a.len ? b.len) return 1; // a>b 5 else if(a.len < b.len) return -1; // a<b 6 else { 7 int i; 8 for(i=0; i<a.len; ++i) { // 从高位到低位比较 9 if(a.d[i] > b.d[i]) return 1; // a>b 10 else if(a.d[i] < b.d[i]) return -1; // a<b 11 } 12 return 0; // 两者相等 13 } 14 }
二、大整数的四则运算
1. 高精度加法
容易得到对其中一位进行加法的步骤:将该位上的两个数字与进位相加,得到的结果取个位数作为该位结果,取十位数作为新的进位。代码如下:
1 // 高精度加法 2 bign add(bign a, bign b) { 3 bign c; 4 int carry = 0; // 进位 5 int i; 6 for(i=0; i<a.len || i<b.len; ++i) { // 以较长的为界限 7 int temp = a.d[i] + b.d[i] + carry; // 其中一位相加 8 c.d[c.len++] = temp%10; // 取个位 9 carry = temp/10; // 新的进位 10 } 11 if(carry != 0) { // 有进位 12 c.d[c.len++] = carry; 13 } 14 return c; 15 }
测试一下加法代码,以下为 C 代码:
1 /* 2 大整数运算 3 */ 4 5 #include <stdio.h> 6 #include <string.h> 7 #include <math.h> 8 #include <stdlib.h> 9 #include <time.h> 10 #include <stdbool.h> 11 12 // 大整数 13 typedef struct{ 14 int d[1000]; 15 int len; 16 // C 中 struct 不能有函数 17 /*bign() { // 构造函数 18 memset(d, 0, sizeof(d)); 19 len = 0; 20 }*/ 21 } bign; 22 23 // 大整数初始化 24 bign init() { 25 bign temp = { {0},0 }; 26 return temp; 27 } 28 29 // 将字符串另存至 bign 结构体 30 bign change(char str[]) { 31 bign a = init(); 32 a.len = strlen(str); // bign 的长度就是数组的长度 33 int i; 34 for(i=0; i<a.len; ++i) { // 倒着赋值 35 a.d[i] = str[a.len-1-i] - ‘0‘; 36 } 37 return a; 38 } 39 40 // 比较两个大整数变量的大小 41 // a 大、相等、小分别返回 1、0、-1 42 int compare(bign a, bign b) { 43 if(a.len > b.len) return 1; // a>b 44 else if(a.len < b.len) return -1; // a<b 45 else { 46 int i; 47 for(i=0; i<a.len; ++i) { // 从高位到低位比较 48 if(a.d[i] > b.d[i]) return 1; // a>b 49 else if(a.d[i] < b.d[i]) return -1; // a<b 50 } 51 return 0; // 两者相等 52 } 53 } 54 55 // 高精度加法 56 bign add(bign a, bign b) { 57 bign c = init(); 58 int carry = 0; // 进位 59 int i; 60 for(i=0; i<a.len || i<b.len; ++i) { // 以较长的为界限 61 int temp = a.d[i] + b.d[i] + carry; // 其中一位相加 62 c.d[c.len++] = temp%10; // 取个位 63 carry = temp/10; // 新的进位 64 } 65 if(carry != 0) { // 有进位 66 c.d[c.len++] = carry; 67 } 68 return c; 69 } 70 71 // 输出大整数 72 void print(bign a) { 73 int i; 74 for(i=a.len-1; i>=0; --i) { 75 printf("%d", a.d[i]); 76 } 77 printf("\n"); 78 } 79 80 int main() { 81 char str1[1000], str2[1000]; 82 scanf("%s%s", str1, str2); 83 bign a = change(str1); 84 bign b = change(str2); 85 print(add(a, b)); 86 87 return 0; 88 }
2. 高精度减法
同样,对其中一位进行减法的步骤:比较被减位和减位,如果不够减,则令被减位的高位减 1、被减位加 10 再进行减法;如果够减,则直接减。最后一步要注意减法后高位可能有多余的 0,要去除它们,但也要保证结果至少有一位数。代码如下:
1 // 高精度减法 2 bign sub(bign a, bign b) { 3 bign c = init(); 4 int i; 5 for(i=0; i<a.len || i<b.len; ++i) { // 以较长的为界限 6 if(a.d[i] < b.d[i]) { // 不够减 7 a.d[i+1]--; 8 a.d[i] += 10; 9 } 10 c.d[c.len++] = a.d[i] - b.d[i]; 11 } 12 while(c.len-1 >= 1 && c.d[c.len-1] == 0) { 13 c.len--; // 去除高位的0,同时至少保留一位最低位 14 } 15 return c; 16 }
最后需要指出,使用 sub 函数前要比较两个数的大小,如果被减数小于减数,需要交换两个变量,然后输出负号,再使用 sub 函数。
3. 高精度与低精度的乘法
所谓的低精度就是可以用基本数据类型存储的数据,例如 int 型。对某一步来说就是这么一个步骤:取 bign 的某位与 int 型整体相乘,再与进位相加,所得结果的个位数作为该位结果,高位部分作为新的进位。代码如下:
1 // 高精度与低精度的乘法 2 bign multi(bign a, int b) { 3 bign c = init(); 4 int carry = 0; // 进位 5 int i; 6 for(i=0; i<a.len; ++i) { 7 int temp = a.d[i] * b + carry; 8 c.d[c.len++] = temp%10; // 取低位 9 carry = temp/10; // 取高位作为新的进位 10 } 11 while(carry != 0) { // carry 可能不止一位 12 c.d[c.len++] = carry%10; 13 carry /= 10; 14 } 15 return c; 16 }
另外,如果 a 和 b 中存在负数,需要先记录下其负号,然后取它们的绝对值代入函数。
4. 高精度与低精度的除法
其中某一步的步骤:上一步的余数乘以 10 加上该步的位,得到该步的临时被除数,将其与除数相除,商为对应的商,余数作为新的余数留作下一步用。代码如下:
1 // r 可以用来储存余数 2 int r = 0; 3 // 高精度与低精度的除法 4 bign divide(bign a, int b) { 5 bign c = init(); 6 c.len = a.len; // 商的每一位与被除数对应 7 int i; 8 for(i=a.len-1; i>=0; --i) { 9 r = r*10 + a.d[i]; 10 c.d[i] = r/b; // 商 11 r %= b; // 新的余数 12 } 13 while(c.len-1 >= 1 && c.d[c.len-1]==0) { 14 c.len--; // 去除高位 0,并保证至少有一位 15 } 16 return c; 17 }
完整的代码如下:
1 /* 2 大整数运算 3 */ 4 5 #include <stdio.h> 6 #include <string.h> 7 #include <math.h> 8 #include <stdlib.h> 9 #include <time.h> 10 #include <stdbool.h> 11 12 // 大整数 13 typedef struct{ 14 int d[1000]; 15 int len; 16 // C 中 struct 不能有函数 17 /*bign() { // 构造函数 18 memset(d, 0, sizeof(d)); 19 len = 0; 20 }*/ 21 } bign; 22 23 // 大整数初始化 24 bign init() { 25 bign temp = { .d={0},.len=0 }; 26 return temp; 27 } 28 29 // 将字符串另存至 bign 结构体 30 bign change(char str[]) { 31 bign a = init(); 32 a.len = strlen(str); // bign 的长度就是数组的长度 33 int i; 34 for(i=0; i<a.len; ++i) { // 倒着赋值 35 a.d[i] = str[a.len-1-i] - ‘0‘; 36 } 37 return a; 38 } 39 40 // 比较两个大整数变量的大小 41 // a 大、相等、小分别返回 1、0、-1 42 int compare(bign a, bign b) { 43 if(a.len > b.len) return 1; // a>b 44 else if(a.len < b.len) return -1; // a<b 45 else { 46 int i; 47 for(i=0; i<a.len; ++i) { // 从高位到低位比较 48 if(a.d[i] > b.d[i]) return 1; // a>b 49 else if(a.d[i] < b.d[i]) return -1; // a<b 50 } 51 return 0; // 两者相等 52 } 53 } 54 55 // 高精度加法 56 bign add(bign a, bign b) { 57 bign c = init(); 58 int carry = 0; // 进位 59 int i; 60 for(i=0; i<a.len || i<b.len; ++i) { // 以较长的为界限 61 int temp = a.d[i] + b.d[i] + carry; // 其中一位相加 62 c.d[c.len++] = temp%10; // 取个位 63 carry = temp/10; // 新的进位 64 } 65 if(carry != 0) { // 有进位 66 c.d[c.len++] = carry; 67 } 68 return c; 69 } 70 71 // 高精度减法 72 bign sub(bign a, bign b) { 73 bign c = init(); 74 int i; 75 for(i=0; i<a.len || i<b.len; ++i) { // 以较长的为界限 76 if(a.d[i] < b.d[i]) { // 不够减 77 a.d[i+1]--; 78 a.d[i] += 10; 79 } 80 c.d[c.len++] = a.d[i] - b.d[i]; 81 } 82 while(c.len-1 >= 1 && c.d[c.len-1] == 0) { 83 c.len--; // 去除高位的0,同时至少保留一位最低位 84 } 85 return c; 86 } 87 88 // 高精度与低精度的乘法 89 bign multi(bign a, int b) { 90 bign c = init(); 91 int carry = 0; // 进位 92 int i; 93 for(i=0; i<a.len; ++i) { 94 int temp = a.d[i] * b + carry; 95 c.d[c.len++] = temp%10; // 取低位 96 carry = temp/10; // 取高位作为新的进位 97 } 98 while(carry != 0) { // carry 可能不止一位 99 c.d[c.len++] = carry%10; 100 carry /= 10; 101 } 102 return c; 103 } 104 105 // r 可以用来储存余数 106 int r = 0; 107 // 高精度与低精度的除法 108 bign divide(bign a, int b) { 109 bign c = init(); 110 c.len = a.len; // 商的每一位与被除数对应 111 int i; 112 for(i=a.len-1; i>=0; --i) { 113 r = r*10 + a.d[i]; 114 c.d[i] = r/b; // 商 115 r %= b; // 新的余数 116 } 117 while(c.len-1 >= 1 && c.d[c.len-1]==0) { 118 c.len--; // 去除高位 0,并保证至少有一位 119 } 120 return c; 121 } 122 123 // 输出大整数 124 void print(bign a) { 125 int i; 126 for(i=a.len-1; i>=0; --i) { 127 printf("%d", a.d[i]); 128 } 129 printf("\n"); 130 } 131 132 int main() { 133 char str1[1000], str2[1000]; 134 scanf("%s%s", str1, str2); 135 bign a = change(str1); 136 bign b = change(str2); 137 print(add(a, b)); 138 print(sub(a, b)); 139 print(multi(a, 11)); 140 print(divide(a, 10)); 141 printf("r = %d\n", r); 142 143 return 0; 144 }