位运算法(a & b) + ((a ^ b) >> 1)可安全计算两整数平均值,避免加法溢出;负数时建议转无符号类型运算;C++20推荐用std::midpoint;浮点转换不可靠。

用 (a & b) + ((a ^ b) >> 1) 算平均值,不溢出
直接写 (a + b) / 2 在 C++ 里对 int 是危险的:只要 a 和 b 同号且绝对值够大,加法就溢出,结果未定义(UB),不是“算错”,是编译器可以随便优化或崩溃。
位运算法绕过加法,天然防溢出:
-
a & b提取两个数都为 1 的位(即进位基础) -
a ^ b是无进位加法,右移 1 位相当于把进位“挪”到正确位置 - 两者相加,就是完整加法,再除以 2?不,这里已经隐含了除以 2——因为进位被右移了
示例:a = INT_MAX, b = 1,(a + b) 溢出;但 (a & b) + ((a ^ b) >> 1) 得到 INT_MAX / 2 + 0,安全。
注意符号:右移在负数时依赖实现
C++ 标准规定 >> 对负数是有符号右移,但语义是“实现定义”的——多数编译器做算术右移(高位补符号位),可接受;但严格来说,它不保证和数学除法一致。
立即学习“C++免费学习笔记(深入)”;
如果你的 a、b 可能为负,更稳妥的做法是先转成无符号类型运算:
- 用
static_cast<unsigned int>(a)</unsigned>和static_cast<unsigned int>(b)</unsigned>转换(假设int是 32 位) - 然后计算
(a_u & b_u) + ((a_u ^ b_u) >> 1) - 最后再转回
int(结果仍在int表示范围内)
这样完全规避符号问题,且仍无溢出风险。
std::midpoint(C++20)是最简解,但要注意兼容性
如果你能用 C++20,直接上 std::midpoint(a, b),它专为这事设计:对整型、浮点、指针都安全,自动处理溢出和符号。
- 对
int,它内部就是类似位运算的实现,非分支、无溢出 - 但 MSVC 2019 默认不开启 C++20,GCC/Clang 需加
-std=c++20 - 旧项目升级前,别默认以为它存在——编译会报
‘midpoint’ is not a member of ‘std’
没有 C++20?别硬凑宏模拟,老老实实自己写位运算版更可控。
别用浮点中间转换,那只是把问题藏起来
有人想“先转 double 再算再转回”,比如 static_cast<int>((static_cast<double>(a) + static_cast<double>(b)) / 2)</double></double></int>:
-
double有 53 位有效精度,而int在 64 位系统可能是 32 位(安全),但在某些嵌入式平台或long long场景下,可能丢失低位 - 更隐蔽的是:当
a和b极大且异号(如INT_MAX和INT_MIN),double相加可能因精度舍入导致结果偏差 ±1 - 还多一次类型转换开销,无必要
位运算或 std::midpoint 是真正从根源掐断溢出,不是用更大容器装小火苗。
最易被忽略的一点:这个技巧只保“平均值”本身不溢出,但如果你后续还要拿结果参与其他运算(比如再乘 3),得单独检查那个新表达式——防溢出不是一劳永逸,是每个算式都要过一遍眼。









