
在java中,使用math.log(n) / math.log(2)计算以2为底的对数并取整来判断2的幂会导致浮点精度误差(如1073741824本应得30,却返回29.999998),从而误判;推荐采用位运算n > 0 && (n & (n - 1)) == 0,高效、精确且无精度损失。
在java中,使用math.log(n) / math.log(2)计算以2为底的对数并取整来判断2的幂会导致浮点精度误差(如1073741824本应得30,却返回29.999998),从而误判;推荐采用位运算n > 0 && (n & (n - 1)) == 0,高效、精确且无精度损失。
判断一个整数是否为2的幂(即形如 $ 2^k $,其中 $ k \geq 0 $ 的正整数),看似简单,但若依赖浮点对数运算,极易因IEEE 754双精度/单精度表示限制而引入致命误差。
例如,当输入 n = 1073741824(即 $ 2^{30} $)时,以下代码会输出 29.999998:
float x = (float)Math.log(n) / (float)Math.log(2); System.out.println(x); // 输出:29.999998 if (x % 1 == 0) return true; // false!导致错误返回
原因在于:
- Math.log() 返回 double,强制转为 float 进一步加剧精度丢失;
- 对数除法结果无法精确表示为二进制浮点数,29.999998 对 x % 1 == 0 判断为 false,直接导致逻辑失败。
✅ 正确解法:位运算(O(1),无精度问题)
public static boolean isPowerOfTwo(long n) {
return n > 0 && (n & (n - 1)) == 0;
}
原理说明:
- 若 n 是2的正整数幂(如 1, 2, 4, 8...),其二进制表示为 1 后跟若干个 0(如 8 → 1000₂);
- n - 1 则为全 1 的低位(如 7 → 0111₂);
- 按位与 n & (n - 1) 必然为 0(唯一例外是 n ≤ 0,故需前置 n > 0 校验)。
| n (十进制) | n (二进制) | n−1 (二进制) | n & (n−1) |
|---|---|---|---|
| 8 | 1000 | 0111 | 0000 ✅ |
| 7 | 0111 | 0110 | 0110 ❌ |
⚠️ 注意事项:
- n 必须为正整数:0 和负数均不满足2的幂定义,n > 0 不可省略;
- 使用 long 类型时,该表达式仍完全适用(& 支持 long);
- 不要尝试用 Math.pow(2, x) == n 或 BigInteger 对数——它们或慢、或仍有精度/性能缺陷;
- 此方法也适用于 int,且被JDK内部(如 HashMap 容量校验)广泛采用,经过充分验证。
总结:浮点运算不适用于离散整数性质判定。当问题本质是二进制结构特征(如2的幂、奇偶性、末位零计数等),优先选择位运算——它更可靠、更快、更简洁。











