在 C++ 中,基础的整数类型(如 int
、long long
)有存储范围的限制:
int
:通常为 32 位,最大值约 21 亿 (2.1 × 109)long long
:通常为 64 位,最大值约 9 百亿亿 (9.2 × 1018)当我们需要处理超出这些范围的超大整数时,比如在以下场景,内置类型就不够用了:
既然无法直接用 long long
存储,我们可以换种方式:
string
):每个字符存储数字的一位。例如 "12345..."。这是最直观的方式。vector<int>
或 int[]
):数组的每个元素存储数字的一位,或者为了优化效率存储多位(如每 4 位或 8 位存一个元素)。通常将数字的**低位存储在数组的低索引**处(如个位在 `a[0]`),方便进位/借位处理。尝试输入两个可能超出 long long
范围的大整数:
模拟 C++ long long
直接相加 (可能溢出):点击计算
使用高精度加法计算结果:点击计算
注意:JavaScript 内置了 BigInt
类型可以处理大整数,这里演示 C++ 的情况。上述“模拟 C++”的结果会显示溢出或错误,而高精度结果将是正确的。
高精度加法的核心思想是**模拟我们手算加法的过程**:列竖式,从个位(最低位)开始,逐位相加,并处理进位。
carry = 0
。a[i] - '0'
, b[i] - '0'
)。如果某个数已经遍历完,则该位视为 0。sum = digit_a + digit_b + carry
。sum % 10
。将其转换为字符后追加到结果字符串。carry = sum / 10
(整数除法)。carry > 0
,需要将这个进位追加到结果字符串的末尾。输入两个非负整数进行加法演示:
高精度减法同样模拟手算减法:列竖式,从个位(最低位)开始,逐位相减。如果不够减,则向高位**借位**。
borrow = 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
转换为字符追加到结果字符串。输入两个非负整数进行减法演示 (确保 A ≥ B):
注意:这里的演示假设被减数大于等于减数。完整的减法函数需要处理小于的情况,通常是交换两者,计算差值,然后添加负号。
请编写一个 C++ 函数 string add(string a, string b)
,接收两个非负整数的字符串表示,返回它们和的字符串表示。考虑输入可能包含前导零(应忽略)以及结果为零的情况。
时间复杂度分析:假设两个数最大长度为 N,你的实现时间复杂度是多少?
复杂度提示:主要操作是逐位相加,循环次数与数字长度相关。因此时间复杂度是 O(N),其中 N 是较长数字的位数。
请编写一个 C++ 函数 string sub(string a, string b)
,接收两个非负整数的字符串表示 `a` 和 `b`,返回 `a - b` 的结果字符串。你需要处理以下情况:
a >= b
:返回差值(注意去除前导零)。a < b
:返回带有负号 -
的差值(即计算 b - a
然后加负号)。复杂度提示:核心仍然是逐位相减,复杂度 O(N)。比较大小也需要 O(N)。处理负号是常数时间。所以总体还是 O(N)。
加减法相对简单,乘除法复杂度更高:
处理高精度小数通常有几种方法:
不像 C++ 需要手动实现或依赖库,许多现代语言内置了大数支持:
int
类型自动支持任意精度整数。运算就像普通整数一样简单。java.math.BigInteger
类用于任意精度整数,java.math.BigDecimal
用于任意精度定点小数。BigInt
类型 (以 `n` 结尾,如 123n
) 用于处理大于 Number.MAX_SAFE_INTEGER
的整数。但 BigInt
和 Number
不能混合运算。System.Numerics.BigInteger
结构。math/big
包提供了 Int
和 Rat
(有理数)。虽然 C++ 标准库没有内置高精度类型,但有强大的第三方库可用:
总结:理解高精度加减法的原理是学习算法和底层实现的好方法。但在实际工程项目中,如果语言或库提供了现成的支持,通常推荐使用它们,因为它们经过了充分测试和优化。