<p>n & (n - 1) == 0 且 n > 0 可判断2的幂,因2的幂二进制仅一个1,n-1将其变为0并右置全1,按位与得0;需排除n≤0,且该法比std::popcount更高效简洁。</p>

为什么 n & (n - 1) 能判断2的幂
因为2的幂在二进制中只有一位是1,其余全为0(如 1、10、100)。而 n - 1 会把该位变成0,并把右边所有位变成1。两者按位与结果必为0。比如 n = 8(1000),n - 1 = 7(0111),n & (n - 1) == 0。
但要注意边界:0不是2的幂,负数也不可能是(题目隐含非负整数)。所以必须先排除 n 。
常见错误是直接写 (n & (n - 1)) == 0,没判零——这会让 n = 0 误判为真。
C++里最安全的单行判断写法
推荐这样写:
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
bool isPowerOfTwo(int n) {
return n > 0 && (n & (n - 1)) == 0;
}理由很实在:
-
n > 0短路保护:避免n == 0时计算n - 1(虽然对int不会溢出,但逻辑上0本就不合法) -
n是有符号整数,负数直接被n > 0挡住,无需额外处理补码问题 - 不依赖
std::popcount或循环,纯位运算,编译器通常能内联成1–2条指令
用 std::popcount 也能做,但没必要
C++20 引入了 std::popcount,它返回二进制中1的个数。于是有人写:
return n > 0 && std::popcount(static_cast<unsigned int>(n)) == 1;
这逻辑没错,但实际更慢且更重:
- 需要类型转换(
int到unsigned int),防止负数传入未定义行为 -
std::popcount在某些平台可能调用库函数,而n & (n - 1)几乎总是编译为单条and指令 - 仅当你要同时判断“是否恰好一个比特位为1”且不关心是否2的幂时,
popcount才有存在意义
注意 int 和 long long 的陷阱
如果输入可能是64位整数,别直接套用 int 版本:
-
long long n时,n & (n - 1)依然有效,但必须用对应无符号类型做popcount(如std::popcount(static_cast<unsigned long>(n))</unsigned>) - 更隐蔽的问题:
n = 1LL 在32位系统上仍是合法 <code>int?不,它超出int范围,会触发未定义行为。务必确认变量类型和取值范围匹配 - 模板化写法可复用,但别为了泛型牺牲可读性——多数场景明确用
int或uint32_t就够了
真正容易被忽略的,是把 n > 0 写成 n >= 1——语义等价,但前者更符合位运算场景的直觉;还有人用 !n & (n - 1),这是错的,! 是逻辑非,优先级高于 &,实际算的是 !(n & (n - 1)),但漏掉了零判断。

















