不能只靠换更大类型,需按运算选策略:加减用std::midpoint或分支,乘除须拆解模运算或用__int128中转,大数应绕过原生类型。

直接结论:不能只靠换更大类型,得根据运算结构选策略——加减用 std::midpoint 或分支判断,乘除必须拆解模运算或用 __int128 做中间容器,大数则绕过原生类型。
什么时候 std::midpoint 能救你一命
它专治「两个整数求中点」时的加法溢出,比如二分查找里写 mid = (left + right) / 2 在 left = INT_MAX - 10、right = INT_MAX 时直接触发未定义行为。而 std::midpoint(left, right) 底层走的是 left + (right - left) / 2 等价逻辑,不依赖加法和,天然避开溢出。
- 必须包含
<numeric></numeric>,且编译器要支持 C++20(GCC/Clang 加-std=c++20,MSVC 用/std:c++20) - 参数类型必须严格一致:
std::midpoint(1, 3L)编译失败,得写成std::midpoint(1LL, 3LL) - 对指针也有效,但两个指针必须指向同一数组,否则行为未定义——这点比整数溢出更难调试
乘法溢出为什么不能靠 long long 一招鲜
50000 × 50000 = 2.5e9,超了 int 上限;但三个 1e6 相乘就是 1e18,刚好卡在 long long 边缘;再多个 10 就崩。这时候换 __int128 是最直接的兜底方案,但它有硬限制:
- 仅 GCC 和 Clang 支持,MSVC 不认;线上 OJ 可能禁用(比如某些老版本 g++ 未开启 128 位支持)
- 标准流不支持直接
cout ,得手写转字符串输出 - 函数参数传递开销略大(16 字节),频繁调用要注意性能
- 别把它当万能药——如果最终结果要存回
int或参与模运算,该拆模还得拆模
模幂、模乘这类运算,中间乘法必须防溢出
写 pow(a, d) % n 或 (a * b) % mod 是典型陷阱。a * b 先溢出,再模就全错。正确做法是把乘法本身做成「加法模拟」或用内置扩展:
- 用
__int128中转:static_cast<__int128>(a) * b % mod</__int128>,前提是a、b、mod都在__int128容量内(即mod 量级) - 手写
mult_mod(a, b, mod):用类似快速幂的思路,把乘法拆成「加 a,模一次;再加 a,再模」,确保每一步中间值 ≤mod - 密码学场景下直接上
Boost.Multiprecision::cpp_int,它重载了%和*,但注意默认不优化模幂,得显式调用boost::multiprecision::powm
真正容易被忽略的点:溢出不是“算错了”,而是“行为未定义”
你以为只是结果变负数?其实编译器可能基于「溢出不会发生」做激进优化。比如这段代码:
if (x > 0 && y > 0 && x * y <p>在 -O2 下,GCC 可能直接删掉整个 if 块——因为它推断 <code>x * y</code> 不可能小于 0,所以条件恒假。这种问题在线上环境才暴露,本地测试永远过。所以关键不是“怎么让结果对”,而是“怎么让编译器不敢乱优化”:该加 <code>volatile</code> 强制读内存,该用 <code>std::add_overflow</code>(C++23)或手动分支判断符号,该关优化就关优化。</p>
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











