上篇我们讲完了输入和输出的内容:
高精度输入输出
从数学角度来说,不管是整数还是小数,不管表示的数字大还是数字小,都应该能够进行四则运算——加减乘除。这几乎是必须的。我们的高精度数字也应该能进行这样的处理。我们把涉及高精度四则运算的算法称之为高精度算法。不论如何,我们开始进行吧。不过,我想还是应该先介绍一下高精度算法包含哪些?
1.高精度加法——两个高精度数字相加
2.高精度减法——两个高精度数字相减(相减结果会大于0
)
3.高精度乘法——两个高精度数字相乘
4.高精度乘法——一个高精度数字乘以常规数字
5.高精度除法——两个高精度数字相除
6.高精度除法——高精度数字除以常规数字
这六种算法我们依次进行介绍。这一篇就来介绍高精度加法。
我们以一个例子的方式来理解吧。计算684930287+501245612
的和。
我想如果你看过前两篇关于高精度的内容,你就会了解到,这时,我们需要两个整数数组,分别存放这两个数字。
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|---|
加数 | 7 | 8 | 2 | 0 | 3 | 9 | 4 | 8 | 6 |
加数 | 2 | 1 | 6 | 5 | 4 | 2 | 1 | 0 | 5 |
它们都是逆向存放的。主要原因是当产生进位时,可以在不移动数组元素的情况下实现。
接下来便是加法了。我们回顾以下小学学习的加法法则。
从个位数字开始,两个对应数位上的数字相加。判断相加的结果是否有进位,如果有进位,则在更高一位的位置添加1
。
高精度数字的加法与小学学习的加法法则相同。
我们从下标为0
的个位数字开始,7
加上2
的结果为9
。不需要进位。下标为1
的8
加上1
结果为9
。仍然不需要进位。下标为2
的2
加上6
结果为8
,不需要进位。下标为3
的0
加上5
结果为5
,不需要进位。下标为4
的3
加上4
结果为7
,不需要进位。下标为5
的9
加上2
结果为11
,需要进位,因此,我们在下一位的位置放置一个进位数字。下标为6
的4
加上1
结果为5
,不需要进位。下标为7
的8
加上0
结果为8
,不需要进位。下标为8
的6
加上5
结果为11
,需要进位,因此,需要在下一位的位置放置一个进位数字。
这样,整个高精度数字的加法完整过程我们就描述清楚了。我们可以使用表格来表示整个过程。
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
加数 | 7 | 8 | 2 | 0 | 3 | 9 | 4 | 8 | 6 | 0 |
加数 | 2 | 1 | 6 | 5 | 4 | 2 | 1 | 0 | 5 | 0 |
进位 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 |
结果 | 9 | 9 | 8 | 5 | 7 | 11-10 | 5+1 | 8 | 11-10 | 0+1 |
这个表格相较于上一个表格,我在右边添加了一列,原因是这个加法结果最高位需要进位,我们需要多提供一个数位。
结果
栏也是显而易见,我使用运算的方式表达了进位的数字——原位上应减去10
,高一位上应加上1
。这里需要特殊说明一下,两个一位数字相加的结果不会超过20
,因此,我们可以使用+1
来表示进位。
我们尝试使用代码来解释这个过程。
//假设a1、a2表示两个高精度数字,其中
//a1 {7, 8, 2, 0, 3, 9, 4, 8, 6, 0}
//a2 {2, 1, 6, 5, 4, 2, 1, 0, 5, 0}
//假设两个高精度数字的数位个数不超过100
//我们使用函数表达高精度数字的加法
void add(int a1[], int a2[])
{
int result[100] = {}; //使用一个数组来存放两个数相加的结果
for(int i = 0; i < 100; ++i)
{
int num = result[i]; //先记录该位置上进位数字
num += a1[i] + a2[i]; //进位加上两个数字之和
if(num >= 10) //达到10,就需要进位
{
num -= 10; //移除进位的部分
result[i + 1] = 1; //下一位进位
}
result[i] = num; //将结果存放在结果数组中
}//经过循环,两个数的和便存放在result数组中
//去前导0
int k = 100 - 1;
while(result[k] == 0 && k > 0) //k > 0,确保一定有数字输出
--k;
for(int i = k; i >= 0; --i)
cout << result[i]; //倒过来输出,中间无需使用空格隔开
}
这样便详细了。但这个程序并不是十分满意。我们在函数内直接输出了结果,有时候我们并不想这样,我们希望在主函数中继续使用相加的结果。例如三个高精度数字相加时,两个数字相加的结果并不能直接输出,而是需要和第三个数相加——684930287+501245612+234871409
.
主函数的代码可能是这样的:
int main()
{
string nums[3] = {"684930287", "501245612", "234871409"}; //这个数组用来存放加数
int result[100] = {}; //用来存放结果
for(int i = 0;i < 3; ++i)
{
int a[100] = {};
//将加数转换为整数数组,并且是逆向存放的
string s = nums[i];
for(int j = 0;j < s.size(); ++j)
a[j] = s[s.size() - 1 - j] - '0';
//高精度加法,相加的结果存放在result中
add(result, a);
}
}
在这种情况,我们上述的代码就有些不太合适了。我们需要做一些改进。下面是改进的版本。
void add(int result[], int a[])
{
for(int i = 0;i < 100; ++i)
{
result[i] += a[i]; //两个数字直接相加,结果存放在result中,存放在i的位置上
//result[i]作为结果,有可能会达到进位的情况
if(result[i] >= 10)
{
result[i] -= 10; //直接使用结果减去10,保留个位数字
result[i + 1] += 1; //进位添加到高位,由于高位原位置存在数字,我们需在原位置上增加1
}
}//经过这层循环,结果都保存在result数组中了,这样主函数中就可以正常使用了。
//由于不用输出,所以不需要除去前导0
}
这样的话,我相信关于高精度数字的加法描述的足够清晰了。