如何存储超出unsigned long long上限的数值解决大数求和溢出问题
问题原因分析
现有代码的异常本质来自两个问题:
- 首先
pow是浮点数运算函数,n较大时本身就会出现精度丢失,哪怕n还没到溢出阈值就可能输出错误的起始、结束数值 - 核心问题是n≥18时,等差数列求和用到的
(finish-start+1)*(start+finish)结果远大于unsigned long long的上限(2^64-1,约1.8e19),溢出截断后就会出现负数、0等异常结果。
解决方案
常用的实现思路有两种:
思路1:引入成熟的大整数运算库
如果允许使用第三方依赖,直接用已经封装好的任意精度整数类型即可,代码改动量最小。比如用Boost库的boost::multiprecision::cpp_int类型,原生支持常规的整数加减乘除运算,不需要自己处理进位逻辑。
修改后的代码示例:
#include <iostream> #include <boost/multiprecision/cpp_int.hpp> using namespace std; using namespace boost::multiprecision; int main() { int n; cin >> n; // 用循环乘10代替pow,避免浮点数精度误差 cpp_int start = 1; for (int i = 0; i < n-1; ++i) start *= 10; cpp_int finish = start * 10 - 1; cpp_int sum = ((finish - start + 1) * (start + finish)) / 2; cout << sum << endl; return 0; }
思路2:手动实现大整数运算(无第三方依赖)
如果不能引入外部库,可以用数组/字符串模拟大整数的运算逻辑。我们可以先简化求和公式减少运算量:
原等差数列的项数为9*10^(n-1),首项加末项为10^(n-1) + (10^n - 1) = 11*10^(n-1) - 1,因此总和公式可以简化为:(9*10^(n-1) * (11*10^(n-1) - 1)) / 2
只需要实现大整数乘小整数、大整数减1、大整数除以2、两个大整数相乘这几个基础逻辑即可,纯原生实现示例如下:
#include <iostream> #include <vector> #include <algorithm> using namespace std; // 大整数(低位在前存储)乘小整数 vector<int> multiply(const vector<int>& num, int x) { vector<int> res; int carry = 0; for (int digit : num) { long long product = (long long)digit * x + carry; res.push_back(product % 10); carry = product / 10; } while (carry > 0) { res.push_back(carry % 10); carry /= 10; } return res; } // 大整数减1 vector<int> subtract_one(const vector<int>& num) { vector<int> res = num; int i = 0; res[i] -= 1; while (i < res.size() && res[i] < 0) { res[i] += 10; i++; res[i] -= 1; } if (res.size() > 1 && res.back() == 0) res.pop_back(); return res; } // 大整数除以2 vector<int> divide_by_two(const vector<int>& num) { vector<int> res; int carry = 0; for (int i = num.size() - 1; i >= 0; --i) { int current = carry * 10 + num[i]; res.push_back(current / 2); carry = current % 2; } reverse(res.begin(), res.end()); while (res.size() > 1 && res.back() == 0) res.pop_back(); return res; } int main() { int n; cin >> n; // 构造10^(n-1),低位在前存储 vector<int> pow10_n_1(n, 0); pow10_n_1[n-1] = 1; // 计算第一个项:9*10^(n-1) auto term1 = multiply(pow10_n_1, 9); // 计算第二个项:11*10^(n-1) -1 auto temp = multiply(pow10_n_1, 11); auto term2 = subtract_one(temp); // 两个大整数相乘 vector<long long> temp_res(term1.size() + term2.size(), 0); for (int i = 0; i < term1.size(); ++i) { for (int j = 0; j < term2.size(); ++j) { temp_res[i+j] += (long long)term1[i] * term2[j]; } } // 处理乘积的进位 vector<int> product; long long carry = 0; for (long long num : temp_res) { long long total = num + carry; product.push_back(total % 10); carry = total / 10; } while (carry > 0) { product.push_back(carry % 10); carry /= 10; } // 除以2得到最终结果 auto sum = divide_by_two(product); // 逆序输出结果 for (int i = sum.size() - 1; i >= 0; --i) { cout << sum[i]; } cout << endl; return 0; }
该实现没有任何外部依赖,只要内存足够,支持的n值可以达到非常大的量级。
补充注意点
不要用pow函数计算整数次幂,浮点数的精度限制会导致n≥17时计算的10^n就已经出现误差,用循环乘法或者大整数幂运算更可靠;另外等差数列的项数和首末项之和必然是一奇一偶,因此除以2的时候不会出现小数,不需要额外处理余数问题。
内容的提问来源于stack exchange,提问作者SmallTimeCppUserr
相关产品推荐
相关产品推荐

