上篇我们讲完了输入和输出的内容:
高精度输入输出
从数学角度来说,不管是整数还是小数,不管表示的数字大还是数字小,都应该能够进行四则运算——加减乘除。这几乎是必须的。我们的高精度数字也应该能进行这样的处理。我们把涉及高精度四则运算的算法称之为高精度算法。不论如何,我们开始进行吧。不过,我想还是应该先介绍一下高精度算法包含哪些?
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 i = 0;i < s.size(); ++i)
a[i] = s[s.size() - 1 - i] - '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; //进位保留在高一位处
}
}//经过这层循环,结果都保存在result数组中了,这样主函数中就可以正常使用了。
//由于不用输出,所以不需要除去前导0
}
这样的话,我相信关于高精度数字的加法描述的足够清晰了。










