n > 0 && (n & (n - 1)) == 0 是判断2的幂最稳妥的方法,因2的幂二进制仅一位为1,n-1使该位变0、右侧全1,按位与为0;但需排除n≤0,避免0误判和负数溢出。

为什么 n & (n - 1) 能判断2的幂
因为2的幂在二进制中只有一位是1,其余全是0(比如 1、10、100)。而 n - 1 会把该位变成0,并把右边所有位变成1。两者按位与结果必为0。但要注意:这个技巧只对正整数有效,0 和负数必须单独排除。
常见错误是直接写 n & (n - 1) == 0,结果 0 也被判为2的幂——实际上 0 不是2的幂,且 n - 1 对 0 是 -1,按位与非零;对负数,符号位参与运算,结果不可靠。
- 必须先检查
n > 0 -
n类型推荐用无符号整数(如unsigned int),避免负数干扰 - 对于
int类型,n == 1是合法的2的幂,n & (n - 1)在此成立
完整可运行的判断函数怎么写
最稳妥的写法是组合条件:正数 + 按位与为0。不依赖编译器对负数的补码细节,也不假设输入范围。
bool isPowerOfTwo(int n) {
return n > 0 && (n & (n - 1)) == 0;
}
如果明确输入为非负整数且可能为0,可改用 unsigned:
bool isPowerOfTwo(unsigned n) {
return n != 0 && (n & (n - 1)) == 0;
}
- 用
!= 0比> 0更自然,因为unsigned不可能小于0 - 不要用
std::popcount(n) == 1——虽然语义清晰,但C++20才支持,且有额外开销 - 避免宏定义(如
#define IS_POW2(n) ((n) & ((n)-1)) == 0),容易因重复求值出错
哪些边界情况会踩坑
实际调试中最常翻车的是这几个值:
-
n = 0:返回true就错了,必须显式排除 -
n = INT_MIN(即-2147483648):n - 1溢出,行为未定义;用unsigned或提前判负可规避 -
n = 1:是2⁰,合法,1 & 0 == 0成立,没问题 - 传入
char或short:隐式提升后运算正常,但若做模板泛型处理,需注意整型提升规则
性能和可读性怎么平衡
位运算方案比循环除2快一个数量级,也比对数函数(log2(n))稳定——后者有浮点精度问题,比如 log2(2^30) 可能算出 29.999999,再取整就错。
- 如果代码可读性优先级更高,且不频繁调用,可用
std::bitset(n).count() == 1(但生成代码更重) - 嵌入式或高频场景坚持用
n > 0 && (n & (n - 1)) == 0,现代CPU一条指令就能完成&,分支预测也友好 - 别为了“炫技”写
!(n & (n - 1)) && n这种表达式——括号易漏,可读性差,还可能被误认为短路逻辑有问题
真正麻烦的不是公式本身,而是忘记 n > 0 这个前提,或者没想清楚类型符号性带来的溢出和解释差异。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











