JavaScript使用填零运算符(>>>)进行右移会产生意外结果

2022-03-01 00:00:00 bit-manipulation javascript

首先,(-1 >>> 0) === (2**32 - 1),我估计是因为左边加了一个新的零,从而将数字转换成33位数字?

但是,为什么(-1 >>> 32) === (2**32 - 1)也是,而我预计它(将32位数字移位32次并将最高有效位替换为0之后)为0。

它不应该等于((-1 >>> 31) >>> 1) === 0吗?还是我错过了什么?


解决方案

执行(-1 >>> 0)时,执行的是无符号右移位。这里没有签名的是钥匙。根据spec,>>>的结果始终是无符号的。-1表示为1的two's compliment。以二进制表示的都是1s(在8位系统中应该是11111111)。

所以现在您通过执行>>> 0使其无签名。您的意思是,将-1的二进制表示(全部为1)移位零位(不做任何更改),但使其返回无符号数字。"因此,您将获得所有1的值。请转到浏览器中的任意javascript控制台并键入:

console.log(2**32 - 1) //4294967295
// 0b means binary representation, and it can have a negative sign
console.log(0b11111111111111111111111111111111) //4294967295
console.log(-0b1 >>> 0) //4294967295
记住2 **任何数字减去1都是二进制形式的全一。这和你提升的两个异能的数量是一样的。因此2**32 - 1等于321。例如,2的3次方(8)减去1(7)等于111,表示为二进制。

因此,下一个(-1 >>> 32) === (2**32 - 1).我们来看几件事。我们知道-1的二进制表示形式都是1s。然后将其右移一位,得到的值与所有1都是一样的,但前面加一个零(并返回一个无符号数字)。

console.log(-1 >>> 1) //2147483647
console.log(0b01111111111111111111111111111111) //2147483647

并不断移动,直到末尾有31个零和一个1

console.log(-1 >>> 31) //1

这对我来说很有意义,我们现在32位有31个0和一个1

那么你碰到了奇怪的大小写,再移位一次应该是零对吗?

根据spec:

6.1.6.1.11 Number::unsignedRightShift ( x, y )
Let lnum be ! ToInt32(x).
Let rnum be ! ToUint32(y).
Let shiftCount be the result of masking out all but the least significant 5 bits of rnum, that is, compute rnum & 0x1F.
Return the result of performing a zero-filling right shift of lnum by shiftCount bits. Vacated bits are filled with zero. The result is an unsigned 32-bit integer.

所以我们知道我们已经有了-1,这都是1两句恭维话。我们将按照文档的最后一步将其移位shiftCount位(我们认为是32位)。和shiftCount为:

让ShiftCount为屏蔽除最低有效5位以外的所有rnum的结果,即计算rnum&;0x1F。

那么rnum & 0x1F是什么呢?好的&表示按位AND操作。lnum>>>左边的数字,rnum是它右边的数字。所以我们说32 AND 0x1F。记住,32是1000000x是十六进制,其中每个字符都可以用4位表示。10001,F是1111。因此0x1F0001111111111(31在基数10中,2**5 - 1也是)。

console.log(0x1F) //31 (which is 11111)

  32: 100000 &
0x1F: 011111
     ---------
      000000
如果为零,则为要移位的位数。这是因为32中的前导1不是5最高有效位的一部分!32是6位。所以我们取32个1并将其移位0位!怪不得。答案仍然是321s。

在示例-1 >>> 31中,这是有意义的,因为31是<= 5位。所以我们做了

  31: 11111 &
0x1F: 11111
      -------
      11111

并将其移位31位.不出所料。

让我们进一步测试一下.让我们做

console.log(-1 >>> 33) //2147483647
console.log(-1 >>> 1)  //2147483647

这说得通,只需将其移位一点。

  33: 100001 &
0x1F: 011111
      ---------
      00001
因此,使用按位运算符检查5位会产生混淆。想要和一个没有研究ECMAScript来回答堆栈溢出帖子的人一起玩树桩假人吗?只需问问为什么这些是相同的。

console.log(-1 >>> 24033) //2147483647
console.log(-1 >>> 1)     //2147483647

当然是因为

console.log(0b101110111100001) // 24033 
console.log(0b000000000000001) // 1
//                      ^^^^^ I only care about these bits!!!    

相关文章