虽然 ECMAScript 中的所有数值都以 IEEE 754 64 位格式存储,但位操作并不直接应用到 64 位,而是先把数值转换为 32 位整数,再进行位操作,操作完成后再把结果转换为 64 位。所以,对我们(开发者)而言,就好像只有 32 位整数一样。
JS 中常常会通过有符号右移 1 位,来得到除以 2 的向下取整结果。
注意是有符号右移,而不是无符号右移!
因为有符号右移 1 位,可以对负数进行除以 2,比如 -27 >> 1
的结果是 -14
。
但在某些特殊情况下,需要进行无符号右移。这个情况就是我们需要对一个大于 2147483647
(也就是 0x7FFF_FFFF
)的正数进行除 2 时,需要使用无符号右移
可以通过程序证明:
for (let i = 0x0FFF_FFFF; i < 0x1_0000_0000; i++) {
if (i >> 1 !== Math.floor(i / 2)) {
throw new Error(`${i} 溢出`)
}
}
// 最终会输出 Error: 2147483648 溢出
而如果使用无符号右移,则最大能够支持对
可以通过程序证明:
for (let i = 0x0FFF_FFFF; i <= 0x1_0000_0000; i++) {
if (i >>> 1 !== Math.floor(i / 2)) {
throw new Error(`${i} 溢出`)
}
}
// 最终会输出 Error: 4294967296 溢出,
// 也就是 0x1_0000_0000 的时候才溢出。
举例说明
为什么说右移是补符号位呢?我们还是拿 -27 举例。
首先,-27 在计算机中的二进制存储是这样的:
1111 1111 1111 1111 1111 1111 1110 0101
而当我们对 -27 进行有符号左移时,它的最右侧的 1 会消失,同时最左侧会空出一个位置
_111 1111 1111 1111 1111 1111 1111 0010 ~1~
那么这个位置需要补什么呢?答案是符号位,由于我们是负数,所以它会补上 1
所以结果就是
1111 1111 1111 1111 1111 1111 1111 0010
这个数字转换为十进制就是 -14。为什么这个二进制是 -14 呢?当然是我们算出来的:
1111 1111 1111 1111 1111 1111 1111 0010 (某个数字的补码形式)
~ 0000 0000 0000 0000 0000 0000 0000 1101 (对其取反)
++ 0000 0000 0000 0000 0000 0000 0000 1110 (然后加一,得到 14)
在前面加个负号
就得到 -14 了
所以我们知道这个补码
的十进制是 -14
既然有符号右移都说了,那再说说无符号右移的。
还是拿 -27 举例,步骤是一样的。
首先,-27 在计算机中的二进制存储是这样的:
1111 1111 1111 1111 1111 1111 1110 0101
而当我们对 -27 进行有符号左移时,它的最右侧的 1 会消失,同时最左侧会空出一个位置
_111 1111 1111 1111 1111 1111 1111 0010 ~1~
那么这个位置需要补什么呢?答案是 0,无论是正数还是负数,都会补上 0
所以,结果会是
0111 1111 1111 1111 1111 1111 1111 0010
而这个数字自己计算一下,就知道是二进制的 2147483634
自己在 js 中运行一下,就可以知道答案
console.log(-27 >>> 1) // 输出 2147483634
原文链接:https://juejin.cn/post/7355414702434467859 作者:Linhieng