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。以二进制表示的都是1
s(在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
的二进制表示形式都是1
s。然后将其右移一位,得到的值与所有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是100000
。0x
是十六进制,其中每个字符都可以用4
位表示。1
是0001
,F是1111
。因此0x1F
是00011111
或11111
(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位!怪不得。答案仍然是321
s。
在示例-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!!!
相关文章