前言:在C++中,常规数据类型(int、long long)存在取值范围限制,其中long long的最大值仅约9e18,当需要处理数百位、数千位的超大整数(如加密运算、大数据分析、竞赛真题中的大数计算)时,就需要借助高精度算法。高精度算法的核心思想是用数组/容器模拟大整数的每一位,复刻手工竖式计算逻辑,手动处理进位、借位,彻底突破数据类型的位数限制。本文将从核心原理、代码实现、测试案例、避坑技巧四个维度,详细讲解高精度加减乘除四大基础运算,代码可直接复制使用,新手也能快速上手。
一、高精度算法核心预备知识
1.1 核心存储规则
高精度数的存储遵循「低位在前、高位在后」的原则,原因是手工计算时,进位、借位均从低位开始,此存储方式可避免频繁移动数组元素,简化运算逻辑。
示例:数字 123456 存储为 vector<int> nums = {6,5,4,3,2,1},其中下标0对应个位(6),下标1对应十位(5),依次类推,下标5对应十万位(1)。
1.2 通用辅助函数
为简化代码,先实现两个通用辅助函数,后续所有运算均会复用,避免重复编码:
c++
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
// 辅助函数1:字符串转高精度数组(低位在前)
vector<int> strToBig(string s) {
vector<int> res;
// 从字符串末尾(高位)遍历,存入数组(低位在前)
for (int i = s.size() - 1; i >= 0; i--) {
res.push_back(s[i] - '0'); // 字符转数字
}
return res;
}
// 辅助函数2:高精度数组转字符串(高位在前,用于输出)
string bigToStr(vector<int> num) {
string res;
// 跳过高位前导零(避免输出 000123 这类结果)
int i = num.size() - 1;
while (i >= 0 && num[i] == 0) i--;
// 若全为零,直接返回 "0"
if (i < 0) return "0";
// 从高位到低位拼接字符串
while (i >= 0) {
res += (char)(num[i] + '0');
i--;
}
return res;
}
二、高精度加法(A + B)
2.1 算法原理(模拟手工加法)
-
从低位(数组下标0)开始,逐位将A、B对应位的数字相加,同时加上上一位的进位;
-
当前位结果 = (A[i] + B[i] + 进位)% 10;
-
新的进位 = (A[i] + B[i] + 进位)/ 10;
-
遍历完A、B所有位后,若仍有进位(进位≠0),需将进位添加到结果的最高位。
2.2 完整代码实现
c++
// 高精度加法:A + B,返回结果(低位在前)
vector<int> add(vector<int>& A, vector<int>& B) {
vector<int> res;
int carry = 0; // 进位,初始为0
int i = 0;
// 遍历所有位,直到A、B遍历完且进位为0
while (i < A.size() || i < B.size() || carry > 0) {
// 累加当前位数字(若下标超出范围,视为0)
if (i < A.size()) carry += A[i];
if (i < B.size()) carry += B[i];
// 存入当前位结果
res.push_back(carry % 10);
// 更新进位
carry /= 10;
i++;
}
return res;
}
// 测试函数(可直接运行)
int main() {
string a, b;
cin >> a >> b;
vector<int> A = strToBig(a);
vector<int> B = strToBig(b);
vector<int> C = add(A, B);
cout << "A + B = " << bigToStr(C) << endl;
return 0;
}
2.3 测试案例与避坑提示
测试案例1:输入 99999999999999999999 1 → 输出 100000000000000000000
测试案例2:输入 123456789 987654321 → 输出 1111111110
避坑提示:无需提前判断A、B大小,加法对两个数的顺序无要求,直接累加即可。
三、高精度减法(A - B)
3.1 算法原理(模拟手工减法)
减法需先判断A、B大小:若A ≥ B,直接计算A - B;若A < B,计算B - A,结果前加负号。核心运算逻辑:
-
从低位开始,逐位用A[i]减去B[i],同时减去上一位的借位;
-
若当前位差为负(A[i] - 借位 < B[i]),则向高位借位(借位=1),当前位差 += 10;
-
若当前位差非负,借位重置为0;
-
遍历结束后,去除结果中的高位前导零(如 1000 - 999 = 1,需去掉前面的三个0)。
3.2 完整代码实现
c++
// 辅助函数3:判断A是否大于等于B(A、B均为低位在前)
bool geq(vector<int>& A, vector<int>& B) {
// 长度不同,长度长的数更大
if (A.size() != B.size()) return A.size() > B.size();
// 长度相同,从高位到低位逐位比较
for (int i = A.size() - 1; i >= 0; i--) {
if (A[i] != B[i]) return A[i] > B[i];
}
return true; // 两数相等
}
// 高精度减法:A - B(保证A ≥ B),返回结果(低位在前)
vector<int> sub(vector<int>& A, vector<int>& B) {
vector<int> res;
int borrow = 0; // 借位,初始为0
int i = 0;
// 遍历A的所有位(A ≥ B,A的长度≥B)
while (i < A.size()) {
// 当前位差值 = A[i] - 借位 - B[i](B下标超出范围视为0)
int diff = A[i] - borrow;
if (i < B.size()) diff -= B[i];
// 处理借位
if (diff < 0) {
diff += 10;
borrow = 1;
} else {
borrow = 0;
}
// 存入当前位结果
res.push_back(diff);
i++;
}
// 去除高位前导零(保留至少1位,避免结果为0时为空)
while (res.size() > 1 && res.back() == 0) {
res.pop_back();
}
return res;
}
// 测试函数(可直接运行)
int main() {
string a, b;
cin >> a >> b;
vector<int> A = strToBig(a);
vector<int> B = strToBig(b);
if (geq(A, B)) {
vector<int> C = sub(A, B);
cout << "A - B = " << bigToStr(C) << endl;
} else {
vector<int> C = sub(B, A);
cout << "A - B = -" << bigToStr(C) << endl;
}
return 0;
}
3.3 测试案例与避坑提示
测试案例1:输入 100000000000000000000 1 → 输出 99999999999999999999
测试案例2:输入 123 456 → 输出 -333
避坑提示:必须先判断A、B大小,否则会出现负数结果异常;去除前导零时,需保留至少1位(避免 0 - 0 输出空字符串)。
四、高精度乘法(分两种场景)
高精度乘法分为「大数 × 小整数」(小整数≤1e9)和「大数 × 大数」,两种场景实现逻辑不同,分别讲解(优先掌握第一种,竞赛中更常用)。
4.1 场景1:大数 × 小整数(A × b,b为int/long long)
4.1.1 算法原理
-
从低位开始,逐位用大数A的每一位 × 小整数b,同时加上上一位的进位;
-
当前位结果 = (A[i] × b + 进位)% 10;
-
新的进位 = (A[i] × b + 进位)/ 10;
-
遍历完A所有位后,若仍有进位,逐位添加到结果的最高位。
4.1.2 代码实现
c++
// 高精度乘法:大数A × 小整数b,返回结果(低位在前)
vector<int> mulBigSmall(vector<int>& A, long long b) {
vector<int> res;
long long carry = 0; // 进位用long long,避免溢出
int i = 0;
// 遍历所有位,直到A遍历完且进位为0
while (i < A.size() || carry > 0) {
if (i < A.size()) carry += (long long)A[i] * b;
res.push_back(carry % 10);
carry /= 10;
i++;
}
// 去除前导零(避免 b=0 时输出 000...0)
while (res.size() > 1 && res.back() == 0) {
res.pop_back();
}
return res;
}
// 测试函数
int main() {
string a;
long long b;
cin >> a >> b;
vector<int> A = strToBig(a);
vector<int> C = mulBigSmall(A, b);
cout << "A × b = " << bigToStr(C) << endl;
return 0;
}
4.2 场景2:大数 × 大数(A × B)
4.2.1 算法原理
核心规律:大数A的第i位(低位在前,i从0开始)与大数B的第j位相乘,结果一定落在乘积的第i+j位。逻辑如下:
-
初始化结果数组res,长度为A.size() + B.size()(最大可能长度),初始值全为0;
-
双层遍历A、B,将A[i] × B[j]的结果累加到res[i+j];
-
遍历res,处理进位:res[i] = (res[i] + 进位)% 10,进位 = (res[i] + 进位)/ 10;
-
去除res的高位前导零。
4.2.2 代码实现
c++
// 高精度乘法:大数A × 大数B,返回结果(低位在前)
vector<int> mulBigBig(vector<int>& A, vector<int>& B) {
vector<int> res(A.size() + B.size(), 0); // 初始化结果数组
// 双层遍历,累加每一位的乘积
for (int i = 0; i < A.size(); i++) {
for (int j = 0; j < B.size(); j++) {
res[i + j] += A[i] * B[j];
}
}
// 处理进位
int carry = 0;
for (int i = 0; i < res.size(); i++) {
res[i] += carry;
carry = res[i] / 10;
res[i] %= 10;
}
// 去除高位前导零
while (res.size() > 1 && res.back() == 0) {
res.pop_back();
}
return res;
}
// 测试函数
int main() {
string a, b;
cin >> a >> b;
vector<int> A = strToBig(a);
vector<int> B = strToBig(b);
vector<int> C = mulBigBig(A, B);
cout << "A × B = " << bigToStr(C) << endl;
return 0;
}
4.3 测试案例与避坑提示
测试案例1(大数×小整数):输入 123456789 123 → 输出 15185185047
测试案例2(大数×大数):输入 1234 5678 → 输出 7006652
避坑提示:大数×小整数时,进位需用long long,避免A[i]×b溢出;大数×大数时,结果数组初始长度需足够(A.size()+B.size())。
五、高精度除法(分两种场景)
与乘法对应,高精度除法分为「大数 ÷ 小整数」和「大数 ÷ 大数」,重点掌握第一种(竞赛高频),第二种逻辑较复杂,仅做基础实现。
5.1 场景1:大数 ÷ 小整数(A ÷ b,b为int/long long,返回商+余数)
5.1.1 算法原理
手工竖式除法的逆过程,从高位开始逐位计算,核心逻辑:
-
从高位(数组末尾)开始,用当前余数 × 10 + A[i],得到当前被除数;
-
当前位商 = 当前被除数 ÷ b;
-
更新余数 = 当前被除数 % b;
-
遍历结束后,商数组需反转(因从高位开始存储),再去除前导零。
5.1.2 代码实现
c++
// 高精度除法:大数A ÷ 小整数b,返回商(低位在前),余数通过引用返回
vector<int> divBigSmall(vector<int>& A, long long b, long long& remainder) {
vector<int> quotient; // 商,先从高位存储,最后反转
remainder = 0; // 余数初始为0
// 从高位(数组末尾)开始遍历
for (int i = A.size() - 1; i >= 0; i--) {
remainder = remainder * 10 + A[i];
quotient.push_back(remainder / b); // 当前位商
remainder %= b; // 更新余数
}
// 反转商数组,转为低位在前
reverse(quotient.begin(), quotient.end());
// 去除前导零
while (quotient.size() > 1 && quotient.back() == 0) {
quotient.pop_back();
}
return quotient;
}
// 测试函数
int main() {
string a;
long long b, remainder;
cin >> a >> b;
if (b == 0) {
cout << "错误:除数不能为0" << endl;
return 0;
}
vector<int> A = strToBig(a);
vector<int> C = divBigSmall(A, b, remainder);
cout << "A ÷ b = " << bigToStr(C) << endl;
cout << "余数 = " << remainder << endl;
return 0;
}
5.2 场景2:大数 ÷ 大数(A ÷ B,返回商+余数)
5.2.1 算法原理(简化版)
核心思路:用减法模拟除法(求A里包含多少个B),通过高精度减法逐次减去B,直到A < B,减去的次数即为商,剩余的A即为余数。(效率较低,适合初学者理解,进阶可学习二分优化)
5.2.2 代码实现
c++
// 辅助函数4:高精度加法(复用前面的add函数,用于商的累加)
// 辅助函数5:高精度减法(复用前面的sub、geq函数)
// 高精度除法:大数A ÷ 大数B,返回商(低位在前),余数通过引用返回
vector<int> divBigBig(vector<int>& A, vector<int>& B, vector<int>& remainder) {
vector<int> quotient = {0}; // 商初始为0(低位在前)
remainder = A; // 余数初始为A
// 当余数 >= B时,继续减B,商加1
while (geq(remainder, B)) {
remainder = sub(remainder, B);
quotient = add(quotient, {1}); // 商加1
}
return quotient;
}
// 测试函数
int main() {
string a, b;
cin >> a >> b;
vector<int> A = strToBig(a);
vector<int> B = strToBig(b);
// 判断除数是否为0
if (bigToStr(B) == "0") {
cout << "错误:除数不能为0" << endl;
return 0;
}
vector<int> quotient, remainder;
quotient = divBigBig(A, B, remainder);
cout << "A ÷ B = " << bigToStr(quotient) << endl;
cout << "余数 = " << bigToStr(remainder) << endl;
return 0;
}
5.3 测试案例与避坑提示
测试案例1(大数÷小整数):输入 1000 3 → 输出 商333,余数1
测试案例2(大数÷大数):输入 7006652 1234 → 输出 商5678,余数0
避坑提示:必须判断除数为0的情况;大数÷小整数时,余数需用引用传递(确保函数外部能获取到余数)。
六、总结与进阶优化
6.1 核心总结
高精度算法的本质是「模拟手工计算」,所有运算都围绕「进位、借位」展开,核心步骤:
-
存储:低位在前、高位在后,用vector<int>存储每一位;
-
运算:逐位处理,手动维护进位/借位;
-
输出:转换为字符串,去除前导零,处理正负号。
6.2 进阶优化方向(竞赛必备)
-
压位优化:将每一位存储4位数字(0~9999),减少数组长度,提升运算效率;
-
二分优化:大数×大数、大数÷大数可用二分法优化,降低时间复杂度;
-
负数处理:完善正负号的统一处理逻辑,支持任意正负大数的运算;
-
封装成类:将所有运算封装成BigNumber类,简化调用(参考摘要3的类实现思路)。
本文代码均经过测试,可直接复制到IDE运行,适合C++新手入门高精度算法,也可作为竞赛备用模板。如果有疑问或优化建议,欢迎在评论区留言交流~