高精度减法 vs 加法

和高精度加法一样,用字符串存数字、数组模拟竖式。区别就两个:进位变借位结果可能是负数


思路拆解

  1. 读入两个字符串
  2. 判断符号:如果被减数小于减数,交换并标记结果为负
  3. 反转字符串(个位对齐下标 0)
  4. 字符转数字
  5. 逐位相减 + 处理借位(不够减就向高位借 1)
  6. 去前导零
  7. 反转输出(负号在前)

完整代码(带注释)

#include <bits/stdc++.h>
using namespace std;
string a, b;
int main() {
    cin >> a >> b;

    // 1. 判断符号:保证 a >= b
    char flag = '+';
    if (a.size() < b.size() || (a.size() == b.size() && a < b)) {
        swap(a, b);
        flag = '-';  // 记下来,输出时用
    }

    // 2. 如果相等,直接输出 0
    if (a == b) {
        cout << '0';
        return 0;
    }

    int MaxLen = a.size();
    vector<int> a1(MaxLen + 1, 0);  // 被减数
    vector<int> a2(MaxLen + 1, 0);  // 减数
    vector<int> a3(MaxLen + 1, 0);  // 结果

    reverse(a.begin(), a.end());
    reverse(b.begin(), b.end());

    // 3. 字符转数字
    for (int i = 0; i < a.size(); i++) a1[i] = a[i] - '0';
    for (int i = 0; i < b.size(); i++) a2[i] = b[i] - '0';

    // 4. 逐位相减 + 借位
    for (int i = 0; i < MaxLen; i++) {
        if (a1[i] < a2[i]) {
            a1[i] += 10;     // 向高位借 10
            a1[i + 1] -= 1;  // 高位减 1
        }
        a3[i] = a1[i] - a2[i];
    }

    // 5. 去前导零
    int index = 0;
    for (int i = MaxLen; i >= 0; i--) {
        if (a3[i] != 0) {
            index = i;
            break;
        }
    }
    a3.resize(index + 1);

    // 6. 反转输出
    reverse(a3.begin(), a3.end());
    if (flag == '-') cout << '-';
    for (int i = 0; i < a3.size(); i++) {
        cout << a3[i];
    }

    return 0;
}

运行示例

输入:
1000
1

输出:
999
输入:
3
5

输出:
-2

常见踩坑指南

🔴 坑 1:忘记负号

减法可能出现负数。比如算 3 - 5

// ✅ 正确 —— 交换前先判断,记下符号
char flag = '+';
if (a.size() < b.size() || (a.size() == b.size() && a < b)) {
    swap(a, b);
    flag = '-';
}
// 输出时
if (flag == '-') cout << '-';

如果不处理,算完输出就少个负号,整个答案都错。

🟡 坑 2:借位后高位变负数

比如 1000 - 1,借位一路滚下去:

a1[0] = 0 < 1 → 向 a1[1] 借位
a1[1] 从 0 变成 -1
a1[1] = -1 < 0 → 又触发借位,向 a1[2] 借
a1[2] 从 0 变成 -1
...

负数会触发下一轮继续借,直到碰到不为 0 的高位为止。这套逻辑能跑通,但写的时候就靠”负数当跳板”,比较隐晦。

更清晰的写法是用借位标记:

int t = 0;  // 借位标记
for (int i = 0; i < MaxLen; i++) {
    a3[i] = a1[i] - a2[i] - t;
    if (a3[i] < 0) {
        a3[i] += 10;
        t = 1;
    } else {
        t = 0;
    }
}

两种都能过题,看你喜欢哪个风格。

🟡 坑 3:前导零清不干净

减法结果可能有好几位前导零,比如 1001 - 1000 = 1,运算完数组是 [1, 0, 0, 0],需要从高位往下扫找到第一个非零位:

int index = 0;
for (int i = MaxLen; i >= 0; i--) {
    if (a3[i] != 0) {
        index = i;
        break;
    }
}
a3.resize(index + 1);  // 只保留有效位

加法和减法对照

加法减法
判断符号不需要✅ 大小判断 + 交换
核心运算a3[i] = a1[i] + a2[i]a3[i] = a1[i] - a2[i]
进位/借位>= 10 向前进 < 0 向前借
去前导零if 最多查 1 位while 可能多位
反转输出一样多一个 -

总结

高精度减法的核心就一句话:

保证大减小 → 借位 → 去零 → 反转 → 别忘负号