n & (n - 1) == 0 且 n > 0 可判断2的幂,因2的幂二进制仅一个1,n-1将其变为0并右置全1,按位与得0;需排除0和负数,适用于各整型,宏定义易出错,推荐inline函数或c++20 std::has_single_bit。

为什么 n & (n - 1) 能判断2的幂
因为2的幂在二进制中只有一个1,比如 1(1)、2(10)、4(100)、8(1000)。而 n - 1 会把那个唯一的1变成0,并把右边所有0变成1。比如 8 是 1000,7 就是 0111,按位与结果为0。
但要注意边界:0不是2的幂,负数也不可能是(题目默认非负整数),所以必须先排除 n 。
-
n == 0时,n & (n - 1)是0 & (-1),结果非零(取决于补码表示,通常不为0),直接错判 - 无符号整数(
unsigned int)下n == 0时n - 1会回绕成全1,n & (n - 1)仍为0,更危险 - 所以安全写法永远是:
n > 0 && (n & (n - 1)) == 0
n & (n - 1) 在不同整数类型下的表现
该表达式对 int、long、long long 都适用,只要类型支持按位与和减法——C++标准保证这些运算对所有整型都定义良好。
但要注意:如果传入 size_t(常见于容器大小或索引),且可能为0,同样要先判正:
bool is_power_of_two(size_t n) {
return n > 0 && (n & (n - 1)) == 0;
}
- 不要用
static_cast<int>(n)</int>强转再算——可能溢出或截断 - 对
uint64_t等固定宽度类型,表达式完全有效,无需额外处理 - 编译器(如 GCC/Clang)对
n > 0 && (n & (n - 1)) == 0通常能内联并优化成极简指令(如单条test+ 条件跳转)
常见误用场景和错误现象
最典型错误是漏判0或负数,导致逻辑崩溃。比如用在位图掩码、内存对齐检查、哈希表扩容条件中,若误将0当作2的幂,后续位操作会出错。
另一个坑是宏定义滥用:
#define IS_POW2(n) ((n) & ((n) - 1)) == 0
- 没加括号保护
n,IS_POW2(a + b)展开后变成(a + b) & ((a + b) - 1) == 0,看似没问题,但若n是表达式带副作用(如i++),会被求值两次 - 没判正,
IS_POW2(0)返回 true,错 - 推荐用 inline 函数替代宏:
inline bool is_pow2(int n) { return n > 0 && (n & (n - 1)) == 0; }
性能和可读性权衡
位运算版本比循环除2快一个数量级,尤其对大数;现代CPU上 n & (n - 1) 是单周期指令,几乎无开销。
但它不够自解释——新成员读到 n & (n - 1) == 0 可能卡顿几秒。所以实际工程中建议:
- 关键路径(如高频调用的内存分配器)用位运算,加一行注释:
// check if n is power of two: only one bit set - 业务逻辑层若性能不敏感,可用
std::has_single_bit(n)(C++20),语义清晰且自动处理0和负数 - 注意:
std::has_single_bit要求n != 0,否则行为未定义——它不帮你判0,这点和手写版本一样需谨慎
真正容易被忽略的是:这个技巧只适用于整数,浮点数不行;而且它依赖二进制表示,对任意进制(比如判断是否为3的幂)完全不适用——别硬套。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











