高精度加减法

任意长度整数的加减运算

为什么需要高精度计算?

在 C++ 中,基础的整数类型(如 intlong long)有存储范围的限制:

当我们需要处理超出这些范围的超大整数时,比如在以下场景,内置类型就不够用了:

如何存储大整数?

既然无法直接用 long long 存储,我们可以换种方式:

尝试输入两个可能超出 long long 范围的大整数:

模拟 C++ long long 直接相加 (可能溢出):点击计算

使用高精度加法计算结果:点击计算

注意:JavaScript 内置了 BigInt 类型可以处理大整数,这里演示 C++ 的情况。上述“模拟 C++”的结果会显示溢出或错误,而高精度结果将是正确的。

高精度加法 (Addition)

高精度加法的核心思想是**模拟我们手算加法的过程**:列竖式,从个位(最低位)开始,逐位相加,并处理进位。

算法步骤 (使用字符串存储)

  1. 将两个大整数(字符串形式)反转,使得个位在前,方便从低位开始处理。
  2. 初始化进位 carry = 0
  3. 从索引 0 开始,逐位遍历两个(反转后的)字符串:
    • 取出当前位的数字 (a[i] - '0', b[i] - '0')。如果某个数已经遍历完,则该位视为 0。
    • 计算当前位的和:sum = digit_a + digit_b + carry
    • 当前位的结果是 sum % 10。将其转换为字符后追加到结果字符串。
    • 新的进位是 carry = sum / 10 (整数除法)。
  4. 遍历结束后,如果最终 carry > 0,需要将这个进位追加到结果字符串的末尾。
  5. 将结果字符串再次反转,得到最终的正确顺序的和。
  6. 注意处理结果可能为 "0" 的情况。

输入两个非负整数进行加法演示:

高精度减法 (Subtraction)

高精度减法同样模拟手算减法:列竖式,从个位(最低位)开始,逐位相减。如果不够减,则向高位**借位**。

算法步骤 (处理非负整数,a >= b)

  1. **比较大小**:首先要确保被减数 `a` 大于等于减数 `b`。如果 `a < b`,则交换 `a` 和 `b`,计算 `b - a`,最后在结果前加上负号(这部分在基础演示中可能简化,但完整实现需要考虑)。
  2. 同样,将两个大整数(字符串)反转。
  3. 初始化借位 borrow = 0
  4. 从索引 0 开始,逐位遍历两个(反转后的)字符串:
    • 取出当前位的数字 (a[i] - '0', b[i] - '0')。b 遍历完则视为 0。
    • 计算被减数当前位受上一位借位影响后的值:digit_a = digit_a - borrow
    • 判断是否需要向更高位借位:如果 digit_a < digit_b,则需要借位。当前位变为 digit_a + 10,同时设置下一位的借位 borrow = 1。否则,borrow = 0
    • 计算当前位的差:diff = digit_a - digit_b
    • 将差值 diff 转换为字符追加到结果字符串。
  5. 遍历结束后,结果字符串可能包含前导零(例如 1000 - 999 = 0001)。需要**去除前导零**:从结果末尾(反转后的最高位)向前查找,直到找到第一个非 '0' 的字符,或者只剩下一个 '0'。
  6. 将处理好前导零的结果字符串再次反转,得到最终答案。

输入两个非负整数进行减法演示 (确保 A ≥ B):

注意:这里的演示假设被减数大于等于减数。完整的减法函数需要处理小于的情况,通常是交换两者,计算差值,然后添加负号。

练习与巩固

选择题

1. 在高精度加法中,为什么要从最低位(个位)开始逐位相加?

A. 为了算法看起来更像手算
B. 为了能正确地将低位的进位传递给高位
C. 从高位开始加也可以,只是编码复杂些
D. 为了方便使用字符串存储

知识延伸

高精度乘法与除法

加减法相对简单,乘除法复杂度更高:

高精度小数

处理高精度小数通常有几种方法:

其他编程语言中的大数支持

不像 C++ 需要手动实现或依赖库,许多现代语言内置了大数支持:

C++ 标准库与第三方库

虽然 C++ 标准库没有内置高精度类型,但有强大的第三方库可用:

总结:理解高精度加减法的原理是学习算法和底层实现的好方法。但在实际工程项目中,如果语言或库提供了现成的支持,通常推荐使用它们,因为它们经过了充分测试和优化。