大整数减法需模拟手算借位,用字符串存储并从低位到高位逐位计算,关键在正确处理借位和去除前导零;先对齐长度,再倒序相减,最后反转结果。

大整数减法的核心逻辑是模拟手算,不是直接用 long long
直接用内置整型做减法会溢出,哪怕 unsigned long long 也只支持到约 1.8×10¹⁹。真正的大整数(比如几百位)必须用字符串或数组存每一位数字,然后从低位到高位逐位借位相减。关键不是“怎么写循环”,而是“怎么处理借位”和“怎么去掉前导零”。
用 std::string 存储时,先对齐再倒序计算最稳妥
常见错误是正着遍历字符串,结果借位逻辑绕晕。正确做法是:
— 把两个字符串转成等长(短的前面补 '0')
— 然后从末尾开始逐位相减(即 i = len - 1 到 0)
— 每次记录当前位结果和借位标志(carry,初值为 0,遇到被减数小于减数+借位时设为 1)
— 结果存入新字符串,最后反转
string subtract(string a, string b) {
// 确保 a >= b(非负结果),否则需额外处理符号
if (a.length() < b.length() || (a.length() == b.length() && a < b)) {
return "-" + subtract(b, a);
}
while (a.length() > b.length()) b = "0" + b;
string res;
int carry = 0;
for (int i = a.length()-1; i >= 0; --i) {
int d1 = a[i] - '0';
int d2 = b[i] - '0';
int diff = d1 - d2 - carry;
if (diff < 0) {
diff += 10;
carry = 1;
} else {
carry = 0;
}
res.push_back('0' + diff);
}
reverse(res.begin(), res.end());
// 去前导零
size_t start = res.find_first_not_of('0');
if (start == string::npos) return "0";
return res.substr(start);
}
注意符号、前导零和边界情况这三类坑
实际用的时候容易栽在这几个点上:
— 不检查大小直接相减,导致结果为负但没加 '-'(上面示例用了递归处理,也可先比较再决定符号)
— 借位后没重置 carry,或者在下一位忘记减去上一轮的 carry
— 返回结果里留着一堆 "000123",得用 find_first_not_of('0') 或手动跳过
— 输入含空格或非法字符,建议调用前用 erase(remove_if(...)) 清理
如果要支持负数和任意大小,得拆成符号+绝对值两步走
单纯实现“非负大整数减法”是教学常用起点,但真实场景中输入可能是 "-123" 或 "456"。这时不能硬套上面逻辑:
— 先解析符号,提取绝对值字符串
— 根据符号组合判断最终运算是加还是减(比如 -a - (-b) 实际是 b - a)
— 复用上面的非负减法或另外实现加法函数
— 最后拼接符号:仅当结果非零且原运算结果为负时才加 '-'
立即学习“C++免费学习笔记(深入)”;
最麻烦的其实是借位链很长时(比如 "1000...000" - "1"),循环里每一步都得严格更新 carry,少一次赋值就全错。别依赖编译器优化,老老实实写清楚每个分支。


















