对一道 A+B 的题目, 如果A 和 B的范围在 int 范围内, 那么将非常简单。但是如果A 和 B是有着1000个数位的整数,就不能简单地用已有的数据类型来表示, 必然会发生溢出。此外, 大整数又称为高精度整数, 其含义就是用基本数据类型无法存储其精度的整数。
大整数的存储:
为了方便随时获取大整数的长度,一般都会定义一个 int 型变量 len 来记录其长度, 并和 d 数组组合成结构体。
struct bignum{
int d[1000];
int len;
bignum(){ //该为构造函数
memset(d, 0, sizeof(d));
len = 0;
}
};
“构造函数”是用来初始化结构体的函数, 函数名和结构体相同, 无返回值。在定义结构体变量之后,它会马上初始化结构体。
memset函数是用来初始化数组,头文件为 string.h
转换:
bignum change(char str[]){
bignum a;
a.len = strlen(str);
for(int i = 0; i < a.len; i++){
a.d[i] = str[a.len - i - 1] - '0'; //逆着赋值
}
return a;
}
比较:
int compare(bignum a, bignum b){
if(a.len > b.len){ // 先比较两个数的位数, 位数长的数更大
return 1;
}else if(a.len < b.len){
return -1;
}else{ // 再从高位往低位比较
for(int i=a.len - 1; i >= 0; i--){
if(a.d[i] > b.d[i]) return 1; // a > b
else if(a.d[i] < b.d[i]) return -1; // a < b
}
return 0; //a = b
}
}
高精度加法:
将该位上的两个数字相加,再与上一位的进位相加,得到的结果取个位数作为该位结果,取十位数作为新的进位。
bignum add(bignum a, bignum b){
bignum c;
int carry = 0;
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){
c.d[c.len++] = carry;
}
return c;
}
高精度减法:
对某一步,比较被减位与减位, 如果不够减,则令被减位的高位减 1, 被减位加 10再进行减法; 如果够减, 则直接减。最后一步要注意减法后高位可能有多余的 0, 要去除它们, 但也要保证结果至少有一位书
bignum sub(bignum a, bignum b){
bignum 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;
}
最后需要注意,使用 sub 函数前要比较两个数的大小, 如果被减数小于减数, 则需要交换两个变量, 然后输出负号,再使用sub函数。
高精度与低精度的乘法
所谓的低精度就是可以用基本数据类型存储的数据, 例如 int 型。这里讲述的就是 bignum 类型 与 int 类型的乘法。
对某一步来说就是这么一个步骤:取 bignum 的某位与 int 型整体相乘, 再与进位相加, 所得结果的个位数作为该位结果,高位部分作为新的进位。
bignum multi(bignum a, int b){
bignum 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){ //和加法不一样,乘法的进位可能不止一位
c.d[c.len++] = carry % 10;
carry /= 10;
}
return c;
}
另外,如果 a 和 b 存在负数, 需要先记录下其负号,然后取它们的绝对值带入函数
高精度与低精度的除法
上一步的余数乘以 10 加上该步的位, 得到该步临时的被除数, 将其与除数比较:如果不够除,则该位的商为 0; 如果够除,则商即为对应的商, 余数即为对应的余数。最后一步要注意减法后高位可能有多余的 0,要去除它们, 但也要保证结果至少有一位数。
bignum divide(bignum a, int b, int& r){ // r 为余数
bignum c;
c.len = a.len; //被除数的每一位数和商的每一位是一一对应的,因此先令长度相等
for(int i = a.len - 1; i >= 0; i--){ //从高位开始
r = r * 10 + a.d[i]; //和上一位遗留的余数组合
if(r < b){ // 不够除,该位为 0
c.d[i] = 0;
}else{ //够除
c.d[i] = r / b; //商
r = r % b; //获得新的余数
}
}
while(c.len - 1 >= 1 && c.d[c.len - 1] == 0){
c.len--; //去除高位的 0,同时至少保留一位最低为 0
}
return c;
}
其完整代码如下:
#include <cstdio>
#include <cstring>
struct bignum{
int d[1000];
int len;
bignum(){ //该为构造函数
memset(d, 0, sizeof(d));
len = 0;
}
};
bignum change(char str[]){
bignum a;
a.len = strlen(str);
for(int i = 0; i < a.len; i++){
a.d[i] = str[a.len - i - 1] - '0'; //逆着赋值
}
return a;
}
int compare(bignum a, bignum b){
if(a.len > b.len){ // 先比较两个数的位数, 位数长的数更大
return 1;
}else if(a.len < b.len){
return -1;
}else{ // 再从高位往低位比较
for(int i=a.len - 1; i >= 0; i--){
if(a.d[i] > b.d[i]) return 1; // a > b
else if(a.d[i] < b.d[i]) return -1; // a < b
}
return 0; //a = b
}
}
bignum add(bignum a, bignum b){
bignum c;
int carry = 0;
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){
c.d[c.len++] = carry;
}
return c;
}
bignum sub(bignum a, bignum b){
bignum 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,同时至少保留一位最低为 0
}
return c;
}
bignum multi(bignum a, int b){
bignum 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){ //和加法不一样,乘法的进位可能不止一位
c.d[c.len++] = carry % 10;
carry /= 10;
}
return c;
}
bignum divide(bignum a, int b, int& r){ // r 为余数
bignum c;
c.len = a.len; //被除数的每一位数和商的每一位是一一对应的,因此先令长度相等
for(int i = a.len - 1; i >= 0; i--){ //从高位开始
r = r * 10 + a.d[i]; //和上一位遗留的余数组合
if(r < b){ // 不够除,该位为 0
c.d[i] = 0;
}else{ //够除
c.d[i] = r / b; //商
r = r % b; //获得新的余数
}
}
while(c.len - 1 >= 1 && c.d[c.len - 1] == 0){
c.len--; //去除高位的 0,同时至少保留一位最低为 0
}
return c;
}
void Print(bignum a){
for(int i= a.len - 1; i >= 0; i--)
printf("%d", a.d[i]);
printf("\n");
}
int main(){
char str1[1000], str2[1000];
scanf("%s%s", str1, str2);
bignum a = change(str1);
bignum b = change(str2);
//高精度加法
printf("a + b = ");
Print(add(a, b));
//高精度减法
printf("a - b = ");
if(compare(a, b) == -1){
printf("-1");
Print(sub(b, a));
}else{
Print(sub(a, b));
}
//高精度与低精度的乘法
int b1 = 0;
for(int i=0; i < strlen(str2); i++){ //将字符串代表的数字转换为 int 类型
b1 = b1 * 10 + (str2[i] - '0');
}
printf("a * b = ");
Print(multi(a, b1));
//高精度与低精度的除法
int r = 0; //余数 r , 即 a % b = r
printf("a / b = ");
Print(divide(a, b1, r));
printf("a % b = %d\n", r);
return 0;
}