主要内容 #
- 进位,错位处理
- 高精度乘法
1. 进位,错位处理 #
1.1高精度-单精度计算 #
先考虑一个简单的情况:乘数中的一个是普通的 int 类型。有没有简单的处理方法呢?
一个直观的思路是直接将a每一位上的数字乘以b。从数值上来说,这个方法是正确的,但它并不符合十进制表示法,因此需要将它重新整理成正常的样子。
重整的方式,也是从个位开始逐位向上处理进位。但是这里的进位可能非常大,甚至远大于9,因为每一位被乘上之后都可能达到9b的数量级。所以这里的进位不能再简单地进行-10运算,而是要通过除以10的商以及余数计算。详见代码注释,也可以参考下图展示的一个计算高精度数1337乘以单精度数42的过程。

当然,也是出于这个原因,这个方法需要特别关注乘数b的范围。若它和 10^9(或相应整型的取值上界)属于同一数量级,那么需要慎用高精度—单精度乘法。
void mul_short(int a[], int b, int c[]) {
clear(c);
for (int i = 0; i < LEN - 1; ++i) {
// 直接把 a 的第 i 位数码乘以乘数,加入结果
c[i] += a[i] * b;
if (c[i] >= 10) {
// 处理进位
// c[i] / 10 即除法的商数成为进位的增量值
c[i + 1] += c[i] / 10;
// 而 c[i] % 10 即除法的余数成为在当前位留下的值
c[i] %= 10;
}
}
}
1.2高精度-高精度 #
如果两个乘数都是高精度,那么竖式乘法又可以大显身手。
于是可以将b分解为它的所有数码,其中每个数码都是单精度数,将它们分别与a相乘,再向左移动到各自的位置上相加即得答案。当然,最后也需要用与上例相同的方式处理进位。

注意这个过程与竖式乘法不尽相同,我们的算法在每一步乘的过程中并不进位,而是将所有的结果保留在对应的位置上,到最后再统一处理进位,但这不会影响结果。
void mul(int a[], int b[], int c[]) {
clear(c);
for (int i = 0; i < LEN - 1; ++i) {
// 这里直接计算结果中的从低到高第 i 位,且一并处理了进位
// 第 i 次循环为 c[i] 加上了所有满足 p + q = i 的 a[p] 与 b[q] 的乘积之和
// 这样做的效果和直接进行上图的运算最后求和是一样的,只是更加简短的一种实现方式
for (int j = 0; j <= i; ++j) c[i] += a[j] * b[i - j];
if (c[i] >= 10) {
c[i + 1] += c[i] / 10;
c[i] %= 10;
}
}
}
2. 高精度乘法 #
请输入两个数,计算它们的积。
【算法分析】
竖式方法举例:
- 25*836
int b[] = {2,5};
int a[] = {8,3,6};
b1 b0
___x_____a2_a1_a0__
c2 c1 c0
d2 d1 d0
___e2_e1_e0________
f5 f4 f3 f2 f1 f0
我们不妨改一下数据的下标,以便于更好的理解下面的代码。
b1 b0
___x_____a2_a1_a0__
c2 c1 c0
d3 d2 d1
___e4_e3_e2________
f5 f4 f3 f2 f1 f0
## 1
fi 的计算过程推导,是代码实现的关键。
1. f的序号和ab有什么关系?为什么是【f序号 = a序号 + b序号】
2. fi的值,是一个累加过程。
## 2
两数乘积的位数,如何确定?
f5 可能没有吗?
【例程】
#include < iostream >
#include < string >
using namespace std;
void multiply(int a[], int b[], int a_len, int b_len, int* f, int& f_len)
{
int i = 0, x = 0;
f_len = 0;
for (int i = 0; i < a_len; ++i)
{
int x = 0; // x 表示进位
for (int j = 0; j < b_len; ++j)
{
// 乘法核心代码
f[i+j] += a[i] * b[j] + x;
x = f[i+j] / 10;
f[i+j] %= 10;
}
f[i + b_len] = x;
}
// 根据最后一位是否为 0 ,判断计算结果的位数
if (f[a_len + b_len - 1] == 0)
f_len = a_len + b_len - 1;
else
f_len = a_len + b_len;
}
int main()
{
// 从屏幕获得输入
string astr, bstr;
cin >> astr;
cin >> bstr;
int lena = astr.size(), lenb = bstr.size();
int a[100] = { 0 }, b[100] = { 0 }; // 假设数字的位数不超过 100
for (int i = 0; i < lena; ++i)
a[lena - i - 1] = astr[i] - '0'; // 将数字倒过来
for (int i = 0; i < lenb; ++i)
b[lenb - i - 1] = bstr[i] - '0';
// 计算高精度乘法
int f[100] = { 0 }, lenf(0);
multiply(a, b, lena, lenb, f, lenf); // 必须是两个非负数,且 a 大于 b
// 输出结果
for (int i = 0; i < lenf; ++i)
{
cout << f[lenf - i - 1];
}
cout << endl;
return 0;
}
习题 #
请将上面的例题,独立完成一遍。要求必须掌握。下节课要求每个人都能够独立写出上面的代码。
课后练习