int和long long无法计算100!因位宽固定(int约2×10⁹,long long约9×10¹⁸),而100!≈9×10¹⁵⁷远超其范围,导致溢出而非精度问题;double会丢失整数精度;手写高精度用vector存低位在前的十进制位,加法逐位进位,乘法用i+j定位并统一进位;boost::cpp_int虽易用但有性能与编译开销;输出需跳过前导零并特判全零。

为什么 int 和 long long 算不了 100!?
因为它们有固定位宽:int 通常最多存 2×10⁹,long long 顶多到 9×10¹⁸。而 100! ≈ 9×10¹⁵⁷,远超任何内置整型。这不是精度问题,是根本存不下——溢出后结果直接错,连警告都不一定有。
- 编译器一般不会对运行时溢出报错(尤其
signed整型,行为未定义) -
unsigned类型溢出是模运算,结果完全不对,但程序照常跑 - 用
double存大整数?小数位会丢失,比如 2⁵³+1 == 2⁵³,已经不是精确整数了
手写高精度加法/乘法最简结构长什么样?
核心就是用 vector<int></int> 存十进制各位(低位在前),模拟小学竖式。不依赖第三方库时,这是最可控、最容易调试的起点。
- 加法:逐位相加 + 进位,最后可能多出一位,
push_back即可 - 乘法:两层循环,
res[i+j] += a[i] * b[j],再统一处理进位 - 别用字符串存数字再转——每次运算都要遍历转换,慢且易错;直接用数字数组更直觉
- 进位必须从低位往高位做,否则高位置更新后影响后续进位计算
// 示例:高精度加法片段
vector<int> add(const vector<int>& a, const vector<int>& b) {
vector<int> c;
int t = 0;
for (int i = 0; i < a.size() || i < b.size() || t; i++) {
if (i < a.size()) t += a[i];
if (i < b.size()) t += b[i];
c.push_back(t % 10);
t /= 10;
}
return c;
}
用 boost::multiprecision::cpp_int 真的省心吗?
它能快速跑通逻辑,但容易忽略隐含成本:临时对象多、内存分配频繁、运算符重载带来间接调用开销。小数据看不出,一算几万位的大数乘法,性能可能比手写慢 3–5 倍。
- 默认构造是动态分配,
cpp_int x = 123;看似简单,背后可能触发堆分配 - 链式运算如
a * b + c * d会产生多个中间cpp_int对象,GC 压力不小 - 头文件巨长(>10MB),编译时间明显变慢,CI 流水线里容易超时
- 跨平台没问题,但若项目禁用 Boost,或只允许 STL,这条路就走不通
输出大数时为什么总多一个前导零?
常见于手写实现中忘记跳过高位零,或者逆序打印时没处理好边界。比如存 123 的数组是 {3,2,1},如果从下标 0 开始全打出来,就会输出 "321";如果没删前导零,可能打出 "000123"。
立即学习“C++免费学习笔记(深入)”;
- 打印前务必从最高非零位开始:倒着遍历,找到第一个非零元素下标
- 特判全零情况(比如 0+0),否则循环会跳过所有位,输出空字符串
- 用
std::to_string拼接每位再反转?效率低,还容易在中间补零——直接按索引倒序输出最稳 - 调试时可在末尾加个标记,比如
cout ,确认是否真没输出
实际写的时候,最难的往往不是算法本身,而是边界条件和内存生命周期——比如临时 vector 在函数返回后是否还有效,或者乘法中间结果要不要 reserve 容量。这些点不报错,但一跑大数据就崩得莫名其妙。









