First, (-1 >>> 0) === (2**32 - 1)
which I expect is due to adding a new zero to the left, thus converting the number into 33-bit number?
But, Why is (-1 >>> 32) === (2**32 - 1)
as well, while I expect it (after shifting the 32-bit number 32 times and replacing the Most Significant Bits with zeros) to be 0.
Shouldn’t it be equal ((-1 >>> 31) >>> 1) === 0
? or Am I missing something?
Advertisement
Answer
When you execute (-1 >>> 0)
you are executing an unsigned right shift. The unsigned here is key. Per the spec, the result of >>>
is always unsigned. -1
is represented as the two’s compliment of 1
. This in binary is all 1
s (In an 8 bit system it’d be 11111111
).
So now you are making it unsigned by executing >>> 0
. You are saying, “shift the binary representation of -1
, which is all 1
s, by zero bits (make no changes), but make it return an unsigned number.” So, you get the value of all 1
s. Go to any javascript console in a browser and type:
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
Remember 2 **
any number minus 1
is always all ones in binary. It’s the same number of ones as the power you raised two to. So 2**32 - 1
is 32 1
s. For example, two to the 3rd power (eight) minus one (seven) is 111
in binary.
So for the next one (-1 >>> 32) === (2**32 - 1)
…. let’s look at a few things. We know the binary representation of -1
is all 1
s. Then shift it right one digit and you get the same value as having all 1
s but precede it with a zero (and return an unsigned number).
console.log(-1 >>> 1) //2147483647 console.log(0b01111111111111111111111111111111) //2147483647
And keep shifting until you have 31 zeros and a single 1
at the end.
console.log(-1 >>> 31) //1
This makes sense to me, we have 31 0
s and a single 1
now for our 32 bits.
So then you hit the weird case, shifting one more time should make zero right?
Per the 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.
So we know we already have -1
, which is all 1
s in twos compliment. And we are going to shift it per the last step of the docs by shiftCount
bits (which we think is 32). And shiftCount
is:
Let shiftCount be the result of masking out all but the least significant 5 bits of rnum, that is, compute rnum & 0x1F.
So what is rnum & 0x1F
? Well &
means a bitwise AND
operation. lnum
is the number left of the >>>
and rnum
is the number right of it. So we are saying 32 AND 0x1F
. Remember 32 is 100000
. 0x
is hexadecimal where each character can be represented by 4
bits. 1
is 0001
and F is 1111
. So 0x1F
is 00011111
or 11111
(31
in base 10, 2**5 - 1
also).
console.log(0x1F) //31 (which is 11111) 32: 100000 & 0x1F: 011111 --------- 000000
The number of bits to shift if zero. This is because the leading 1
in 32
is not part of the 5
most significant bits! 32
is six bits. So we take 32 1
s and shift it zero bits! That’s why. The answer is still 32 1
s.
On the example -1 >>> 31
this made sense because 31 is <= 5
bits. So we did
31: 11111 & 0x1F: 11111 ------- 11111
And shifted it 31
bits…. as expected.
Let’s test this further…. let’s do
console.log(-1 >>> 33) //2147483647 console.log(-1 >>> 1) //2147483647
That makes sense, just shift it one bit.
33: 100001 & 0x1F: 011111 --------- 00001
So, go over 5
bits with a bitwise operator and get confused. Want to play stump the dummy with a person who hasn’t researched the ECMAScript to answer a stackoverflow post? Just ask why are these the same.
console.log(-1 >>> 24033) //2147483647 console.log(-1 >>> 1) //2147483647
Well of course it’s because
console.log(0b101110111100001) // 24033 console.log(0b000000000000001) // 1 // ^^^^^ I only care about these bits!!!