<p>n & (n - 1) == 0 能判断正整数是否为2的幂,因其二进制仅含一个1,n-1使该位变0、后续位全1,按位与得0;但必须先确保n > 0,否则0和负数会误判。</p>

为什么n & (n - 1)能判断2的幂
因为2的幂在二进制中只有一个1,比如8是1000,16是10000。而n - 1会把这个1变成0,后面所有0变成1——8 - 1 = 7即0111。两者按位与结果必为0。
但这个技巧有个致命前提:n > 0。负数、零、1都要单独考虑。
n 直接返回<code>false(0不是2的幂,负数更不是)-
n == 1是合法的2的幂(2^0),但1 & 0 == 0也成立,所以只要加n > 0判断就能覆盖 - 无符号整数(
unsigned int)可省去n > 0检查,但实际传入int时仍需防范负值
n & (n - 1) == 0的边界陷阱
最常踩的坑是没处理n == 0:因为0 & (-1)在补码下等于0(-1全为1),导致0被误判为2的幂。
另一个隐性问题是整数溢出:对INT_MIN(如-2147483648)做n - 1会溢出,但此时n已是负数,早该被n > 0拦住。
立即学习“C++免费学习笔记(深入)”;
- 错误写法:
return (n & (n - 1)) == 0;→0和负数都可能过 - 正确写法:
return n > 0 && (n & (n - 1)) == 0; - 若输入类型是
unsigned int,可简化为return n && !(n & (n - 1));(n非零即真)
不同整数类型的兼容写法
用auto或模板泛型时,要确保n - 1不触发未定义行为。比如uint8_t n = 0,n - 1会回绕成255,n & (n - 1)变成0 & 255 == 0,又误判了。
所以类型无关的安全写法必须显式检查零值,且优先用无符号类型入参。
- 推荐签名:
bool is_power_of_two(unsigned int n) - 模板版本需加
std::is_unsigned_v<t></t>静态断言,或对有符号类型先转为对应无符号类型再运算 - 对
long long等大类型,位运算本身无性能差异,但n > 0检查仍不可少
性能对比:位运算 vs 循环 vs __builtin_popcount
位运算方案是O(1),无分支(现代CPU下预测友好),比循环除2快一个数量级。但要注意编译器优化级别:
-
gcc -O2下n > 0 && !(n & (n - 1))会被内联为3–4条指令 -
__builtin_popcount(n) == 1虽直观,但底层调用POPCNT指令——在不支持该指令的老CPU上会降级为软件实现,反而慢 - 用
log2(n)取整再验证?浮点运算+取整+精度误差,完全不推荐
真正容易被忽略的是:这个表达式只适用于整数,且必须保证输入在有效范围内;一旦从用户输入或文件读取,务必先做范围校验,否则n - 1的溢出行为取决于类型和平台。


















