在 C++ 中,基本数据类型(如int、long long)的取值范围是有限的(例如long long最大只能表示约 9e18 的整数)。当需要处理远超这一范围的大整数(如数百位甚至数千位的数字)时,就需要借助高精度计算技术。本文将详细解析高精度计算的核心思想,实现高精度整数的加减乘除运算,并探讨其实际应用场景。
一、高精度计算的核心思想
高精度计算的本质是用数组或字符串模拟大整数的存储与运算过程,还原手工计算的逻辑。其核心要点包括:
-
数据存储:
- 通常用数组 存储大整数的每一位(如
vector<int> num) - 为方便计算,低位在前、高位在后(例如
123存储为[3,2,1])
- 通常用数组 存储大整数的每一位(如
-
运算模拟:
- 模拟手工计算的步骤(如加法中的进位、乘法中的错位相加)
- 通过循环处理每一位,用变量记录进位或借位
-
输入输出处理:
- 输入时从字符串转换为数组(注意反转顺序)
- 输出时从数组高位到低位依次打印
二、高精度加法
2.1 算法原理
两个大整数相加,从最低位开始逐位相加,同时处理进位:
- 每一位的和 = 两数对应位的值 + 进位
- 当前位结果 = 和 % 10
- 新的进位 = 和 / 10
- 最后若仍有进位,需添加到结果的最高位
2.2 代码实现
cpp
运行
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 高精度加法:返回 a + b 的结果
vector<int> add(vector<int>& a, vector<int>& b) {
vector<int> res;
int carry = 0; // 进位
int i = 0;
// 处理所有位(包括进位)
while (i < a.size() || i < b.size() || carry > 0) {
int sum = carry;
if (i < a.size()) sum += a[i];
if (i < b.size()) sum += b[i];
res.push_back(sum % 10); // 当前位结果
carry = sum / 10; // 更新进位
i++;
}
return res;
}
// 辅助函数:字符串转高精度数组(低位在前)
vector<int> str_to_big(string s) {
vector<int> res;
for (int i = s.size() - 1; i >= 0; i--) {
res.push_back(s[i] - '0');
}
return res;
}
// 辅助函数:高精度数组转字符串(高位在前)
string big_to_str(vector<int>& num) {
string res;
// 跳过前导0(如果有的话)
int i = num.size() - 1;
while (i >= 0 && num[i] == 0) i--;
// 转换有效数字
while (i >= 0) {
res += (char)(num[i] + '0');
i--;
}
return res.empty() ? "0" : res; // 处理全0的情况
}
int main() {
string s1, s2;
cin >> s1 >> s2;
vector<int> a = str_to_big(s1);
vector<int> b = str_to_big(s2);
vector<int> c = add(a, b);
cout << big_to_str(c) << endl; // 输出结果
return 0;
}
2.3 测试案例
plaintext
输入:99999999999999999999 1
输出:100000000000000000000
三、高精度减法
3.1 算法原理
两个大整数相减(假设a >= b),从最低位开始逐位相减,处理借位:
- 每一位的差 = 被减数对应位 - 减数对应位 - 借位
- 若差为负,需借位(差 += 10,借位 = 1)
- 否则借位 = 0
- 最后需去除结果中的前导 0
3.2 代码实现
cpp
运行
// 判断 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; // 借位
int i = 0;
while (i < a.size() || i < b.size()) {
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++;
}
// 去除末尾的0(前导0,因低位在前存储)
while (res.size() > 1 && res.back() == 0) {
res.pop_back();
}
return res;
}
// 主函数中使用时需先判断大小:
// if (geq(a, b)) {
// cout << big_to_str(sub(a, b)) << endl;
// } else {
// cout << "-" << big_to_str(sub(b, a)) << endl;
// }
3.3 测试案例
plaintext
输入:100000000000000000000 1
输出:99999999999999999999
四、高精度乘法(大整数 × 小整数)
4.1 算法原理
大整数与小整数(int范围内)相乘:
- 从最低位开始,每一位与小整数相乘
- 乘积 = 当前位值 × 小整数 + 进位
- 当前位结果 = 乘积 % 10
- 新的进位 = 乘积 / 10
- 最后处理剩余进位
4.2 代码实现
cpp
运行
// 高精度乘法:返回 a × b 的结果(b 是小整数)
vector<int> mul_big_small(vector<int>& a, int b) {
vector<int> res;
int carry = 0;
int i = 0;
while (i < a.size() || carry > 0) {
long long product = (long long)carry; // 用long long避免溢出
if (i < a.size()) product += (long long)a[i] * b;
res.push_back(product % 10);
carry = product / 10;
i++;
}
// 去除前导0
while (res.size() > 1 && res.back() == 0) {
res.pop_back();
}
return res;
}
4.3 测试案例
plaintext
输入:99999999999999999999 2
输出:199999999999999999998
五、高精度乘法(大整数 × 大整数)
5.1 算法原理
两个大整数相乘,模拟手工乘法的错位相加:
- 设
a的长度为n,b的长度为m,则结果最大长度为n + m a[i] × b[j]的结果应存放在res[i + j]的位置- 先计算所有位的乘积和,再统一处理进位
5.2 代码实现
cpp
运行
// 高精度乘法:返回 a × b 的结果(均为大整数)
vector<int> mul_big_big(vector<int>& a, vector<int>& b) {
int n = a.size(), m = b.size();
vector<int> res(n + m, 0); // 初始化为0
// 计算所有位的乘积
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
res[i + j] += a[i] * b[j]; // 累加乘积到对应位置
}
}
// 处理进位
int carry = 0;
for (int i = 0; i < res.size(); i++) {
int total = res[i] + carry;
res[i] = total % 10;
carry = total / 10;
}
// 去除前导0
while (res.size() > 1 && res.back() == 0) {
res.pop_back();
}
return res;
}
5.3 测试案例
plaintext
输入:123456789 987654321
输出:121932631112635269
六、高精度除法(大整数 ÷ 小整数)
6.1 算法原理
大整数除以小整数(int范围内),模拟手工除法:
- 从最高位开始,逐位与除数相除
- 每一步的被除数 = 上一步的余数 × 10 + 当前位
- 当前位商 = 被除数 / 除数
- 新的余数 = 被除数 % 除数
- 最后返回商和余数
6.2 代码实现
cpp
运行
// 高精度除法:返回商,余数通过引用传出(a ÷ b)
vector<int> div_big_small(vector<int>& a, int b, int& remainder) {
vector<int> quotient;
remainder = 0;
// 从高位到低位计算(因存储是低位在前,需反向遍历)
for (int i = a.size() - 1; i >= 0; i--) {
long long dividend = (long long)remainder * 10 + a[i];
quotient.push_back(dividend / b);
remainder = dividend % b;
}
// 反转商(因计算时是从高位到低位,存储需转为低位在前)
reverse(quotient.begin(), quotient.end());
// 去除前导0
while (quotient.size() > 1 && quotient.back() == 0) {
quotient.pop_back();
}
return quotient;
}
6.3 测试案例
plaintext
输入:123456789 3
输出:41152263(商),0(余数)
七、高精度计算的优化与扩展
7.1 存储优化:压缩存储
上述实现中每个数组元素只存 1 位数字,空间利用率低。可优化为每元素存储多位数字(如 4 位,对应 0~9999),减少数组长度和循环次数:
cpp
运行
// 示例:每元素存储4位数字,123456789 存储为 [6789, 2345, 1]
vector<int> str_to_big_compressed(string s) {
vector<int> res;
int n = s.size();
for (int i = n - 1; i >= 0; i -= 4) {
int val = 0;
int start = max(0, i - 3); // 处理不足4位的情况
for (int j = start; j <= i; j++) {
val = val * 10 + (s[j] - '0');
}
res.push_back(val);
}
return res;
}
7.2 性能优化:快速傅里叶变换(FFT)
对于超大整数(万位以上)的乘法,普通 O (nm) 算法效率极低。可采用FFT 加速多项式乘法,将时间复杂度降至 O (n log n),但实现复杂,适合对性能要求极高的场景。
7.3 扩展功能
- 支持小数高精度计算(需记录小数点位置)
- 实现模运算(大整数取模)
- 支持负数运算(增加符号位标记)
八、高精度计算的应用场景
- 密码学:RSA 等加密算法依赖大整数的模幂运算(如 1024 位整数的乘法)。
- 数学研究:处理超大数(如阶乘、斐波那契数列的高项)。
- 金融计算:精确计算大额资金的利息、汇率转换(避免浮点数误差)。
- 编程竞赛:解决涉及大整数的算法题(如高精度递推、大数质因数分解)。
九、常见问题与注意事项
- 前导零处理:运算后需去除结果中的前导零(尤其是减法和除法)。
- 溢出问题 :中间计算需用
long long临时存储,避免整数溢出。 - 空值处理:输入为 "0" 时需特殊处理,避免转换后数组为空。
- 性能瓶颈:大整数乘法的时间复杂度较高,需根据数据规模选择合适算法。
十、总结
高精度计算通过模拟手工运算的逻辑,突破了基本数据类型的取值限制,实现了任意长度整数的四则运算。其核心是合理的存储结构 (低位在前的数组)和细致的进位 / 借位处理。
在 C++ 中实现高精度计算时,需注意边界条件(如前导零、空输入)和性能优化(如压缩存储)。虽然标准库中没有内置的高精度类型,但通过自定义数组操作,可灵活实现各类高精度运算,满足密码学、数学研究等场景的需求。
掌握高精度计算不仅能解决技术问题,更能培养 "将复杂问题分解为简单步骤" 的算法思维,这是编程能力的重要体现。