(-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?
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
1s (In an 8 bit system it’d be
So now you are making it unsigned by executing
>>> 0. You are saying, “shift the binary representation of
-1, which is all
1s, by zero bits (make no changes), but make it return an unsigned number.” So, you get the value of all
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 ** 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
1s. 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
1s. Then shift it right one digit and you get the same value as having all
1s 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
0s 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:
126.96.36.199.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
1s 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
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
lnum is the number left of the
rnum is the number right of it. So we are saying
32 AND 0x1F. Remember 32 is
0x is hexadecimal where each character can be represented by
0001 and F is
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
32 is not part of the
5 most significant bits!
32 is six bits. So we take 32
1s and shift it zero bits! That’s why. The answer is still 32
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!!!