为何位计数函数在处理超过32位的数值时失效?
为什么计算2^i-1的二进制1的位数时,i≥32会出现错误结果?
嘿,这个问题其实是JavaScript里经典的数值精度陷阱,根源在于JavaScript的Number类型本质是64位双精度浮点数,它能精确表示的整数范围是有限的。
让我拆解一下原因:
双精度浮点数的精确边界:双精度用52位存储尾数(加上一个隐藏的最高位1,总共53位有效数字),这意味着它只能精确表示所有绝对值≤
2^53的整数。超过这个范围的整数,无法被精准存储,会被近似到最近的可表示值。分阶段看你的测试结果:
- 当
0 ≤ i ≤ 31:2^i-1是i位全1的整数,远小于2^53,完全在精确范围内,所以你的代码能正确算出count=i,二进制输出也没问题。 - 当
32 ≤ i ≤ 53:理论上2^i是2的幂,刚好能被双精度精确表示(因为2的幂只需要调整指数位,尾数全0),而2^i-1是i位全1的整数,有效数字位数≤53,也能被精确存储。所以这时候你的代码应该能得到正确结果(count=i)。如果你测试时i=32就出错,大概率是测试环境的特殊情况,或者是执行时的偶发问题——标准JS环境下这一段是没问题的。 - 当
i > 53:这时候2^i-1是i位全1的整数,有效数字位数超过了53位的限制,无法被精确存储。此时Math.pow(2, i) - 1的结果会被近似成2^i(因为这是离它最近的可表示值)。既然num实际上等于2^i,它的二进制就是1后面跟着i个0,执行n = n & (n - 1)时第一次操作就把唯一的1消掉了,所以count=1,toString(2)也会输出错误的二进制串,这就是你看到的情况。
- 当
如果要处理超过2^53的整数,推荐用JavaScript的BigInt类型,它支持任意精度的整数运算。修改后的代码如下:
for (let i = 0; i < 60; i++) { let count = 0 const num = (2n ** BigInt(i)) - 1n // 用BigInt做高精度运算 let n = num while (n > 0n) { n = n & (n - 1n) count++ } console.log(`The binary representation of the number 2^${i}-1 contains ${count} '1', binary: ${num.toString(2)}`) }
这样不管i多大,都能得到正确结果,因为BigInt不会有精度损失。
内容的提问来源于stack exchange,提问作者user15163984
相关产品推荐
相关产品推荐

