n & (n - 1) 能判断2的幂,因为2的幂在二进制下仅含一个1(如8=1000),n−1会将其变为全1(如7=0111),按位与得0;但需排除n=0(非2的幂),而n=1(2⁰)合法,故最终条件为n > 0且n & (n−1) == 0。

为什么 n & (n - 1) 能判断 2 的幂
因为 2 的幂在二进制下只有一个 1,比如 8 是 1000,16 是 10000。而 n - 1 会把这个唯一的 1 变成 0,右边所有位变成 1:比如 8 - 1 = 7 → 0111。两者按位与结果必为 0。
但要注意边界:只有正整数才适用。负数、零、1 需单独处理——1 是 2^0,合法;0 不是 2 的幂,必须排除。
n 直接返回 <code>false- 对正整数,检查
n & (n - 1) == 0 - 这个表达式在 x86/x64 上通常编译为 2–3 条指令,比循环除 2 快一个数量级
n == 0 是最常被忽略的错误输入
很多初学者写成 return (n & (n - 1)) == 0,结果 n = 0 时:0 & (-1) 在补码下等于 0,误判为 true。这是线上 bug 的高发点。
实际场景中,比如用 bitset 做哈希桶大小校验、内存对齐检查、FFT 长度验证,输入可能来自用户或配置文件,0 很容易混进来。
- 必须前置判断
n ,不能只依赖位运算 - 若函数签名是
unsigned int n,可省略,但需确认调用方绝不会传 <code>0 - 用
static_cast<unsigned int>(n)</unsigned>强转有风险:若原n是负数,转成大正数后仍可能满足n & (n-1) == 0(如-2147483648)
如何安全支持 int、long long 和无符号类型
模板函数最稳妥,避免隐式转换出错。直接对 long long 用 n & (n-1) 没问题,但要注意 n = 1LL 这种边界值——<code>n-1 不溢出,位运算是安全的。
不要用 std::bitset 或 __builtin_popcount 替代:前者开销大,后者依赖 GCC 且要额外计数,违背“极速”初衷。
- 推荐写法:
template<typename t> constexpr bool is_power_of_two(T n) { return n > 0 && (n & (n - 1)) == 0; }</typename> - 若限定 C++20,可用
<bit></bit>中的std::has_single_bit(n),语义更清晰,但底层仍是相同位运算 - 对
uint8_t等窄类型,确保运算前提升到足够宽度(编译器通常自动处理)
为什么不用 log2(n) 或查表法
log2 涉及浮点运算和舍入误差,比如 log2(256) 可能算出 7.9999999,floor 后变 7,误判失败。而且浮点指令延迟远高于整数位运算。
查表法(如 32 位预存 32 个 2 的幂)看似快,但 cache miss 开销大,且无法泛型适配不同整型宽度。
- 位运算方案在任何现代 CPU 上都是单周期或接近单周期
- 编译器(GCC/Clang/MSVC)对
n & (n-1)有成熟优化,无需手动内联 - 唯一真正要注意的,是别忘了
n > 0这个前提——它不是技巧的一部分,而是逻辑必要条件
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











