n > 0 && !(n & (n - 1)) 能正确判断正整数 n 是否为 2 的幂:因 2 的幂二进制仅一个 1,n−1 将其变为 0 且右侧全 1,按位与得 0;n ≤ 0 需前置排除,确保安全高效。

为什么 n & (n - 1) 能判断 2 的幂
2 的幂在二进制下只有一个 1:比如 1(1)、2(10)、4(100)、8(1000)。而 n - 1 会把那个唯一的 1 变成 0,右边所有位全变 1。所以 n & (n - 1) 必然为 0 —— 前提是 n > 0。
注意:这个技巧对 n = 0 失效,因为 0 & (-1) 在补码下不为 0;负数也不适用(2 的幂定义为正整数)。
-
n必须是无符号整数或已确认为正的有符号整数 - 要排除
0,必须显式检查n > 0 - 对
int类型,n = INT_MIN时n - 1不会溢出导致未定义行为,但INT_MIN本身不是 2 的幂,且INT_MIN & (INT_MIN - 1)≠ 0,所以逻辑仍安全——只要先判正
一行安全写法:(n > 0) && !(n & (n - 1))
这是最常用、最稳妥的表达式,兼顾正确性与可读性。编译器能高效优化为几条位指令,没有分支预测开销。
示例:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
bool isPowerOfTwo(int n) {
return (n > 0) && !(n & (n - 1));
}
- 不要写成
n & (n - 1) == 0—— 运算符优先级会导致等价于n & ((n - 1) == 0),逻辑错误 - 用
!而非== 0更符合位运算语义,也避免隐式类型转换歧义 - 若输入类型是
unsigned int,可省略n > 0,但建议保留以明确语义
边界情况:n = 1 和 INT_MIN
1 是 2⁰,合法的 2 的幂。1 & 0 是 0,配合 n > 0 判断完全正确。
INT_MIN(如 -2147483648)是负数,n > 0 直接拦截,不会进入位运算分支,避免了符号位参与带来的混淆。
- 别依赖
std::bitset或__builtin_popcount来数 1 的个数——那要遍历位,O(log n),不是“极速版” - 某些平台对
n = 0执行n & (n - 1)可能触发未定义行为(如生成非法指令),所以n > 0不是可选,是必须 - 若你确定输入永远是非零正整数(例如枚举 size_t 对齐检查),可只用
!(n & (n - 1)),但文档里得写死这个前提
为什么不用 log2(n) 或循环除 2
log2 涉及浮点运算、舍入误差和函数调用开销;循环除 2 最坏要迭代 32/64 次,而位运算是单周期指令(现代 CPU 上)。
-
log2(1) == 0.0,但log2(0)是 -inf,log2(负数)是 NaN,需要额外容错 - 循环写法如
while (n % 2 == 0) n /= 2; return n == 1;在n = 0时死循环 - 即使加保护,分支预测失败时性能抖动明显,不适合高频调用场景(如内存分配器对齐检查)
真正快的不是“看起来短”,而是不依赖数据分布、无分支、无函数调用、CPU 流水线友好的那一种。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










