A bit of << and >> before
Before we discuss >>>
, we need a small introduction for <<
(left shift) and >>
(right shift) operators.
They shift the bits of the left operand by the number of places specified in the right operand in the direction of arrows.
Example: 0010 << 1
will result in 0100
and 0100 >> 1
will be 0010
.
Importantly, they preserve the sign of the number. That means negative numbers stay negative after the shift.
>>>
>>> operator is called "unsigned right shift operator" or "zero-fill right shift operator".
If you do x >>> y
, it shifts y number of bits to the right and fills 0s from left. The bits pushed out from the right are discarded. And, it doesn't preserve the sign after the shift.
>> vs >>>
A negative sign (-) is represented by setting the left most bit as 1. When >>
is used on a negative number, the result will again get a 1 as the left most bit to preserve the sign. But, >>>
pushes 0s from left even on negative numbers without preserving the sign bit. So, the result is always going to be a positive number.
When you observe the following, you'll see >>
always keeps 1 as the left bit if there was a 1 before.
10000000000000000000000000000010 >> 1; // 11000000000000000000000000000001
10000000000000000000000000000010 {'>>>'} 1; // 01000000000000000000000000000001
10000000000000000000000000000100 >> 2; // 11100000000000000000000000000001
10000000000000000000000000000100 {'>>>'} 2; // 00100000000000000000000000000001
10000000000000000000000000100000 >> 4; // 11111000000000000000000000000010
10000000000000000000000000100000 {'>>>'} 4; // 00001000000000000000000000000010
>>> 0
Let's try not shifting using shift operators.
1 >> 0; // 1 Ok
-1 >> 0; // -1 Looks good
1 {'>>>'} 0; // 1 Makes sense
-1 {'>>>'} 0; // 4294967295 I'm JavaScript
Wait, what?
How is that if I shift zero bits, I get a large number?
To understand that, let's use toString(2)
to see the results in binary.
(1 >> 0).toString(2); // 1
(-1 >> 0).toString(2); // -1
(1 {'>>>'} 0).toString(2); // 1
(-1 {'>>>'} 0).toString(2); // 11111111111111111111111111111111 (4294967295 in decimal)
Negative numbers are generally represented with 2's complement.
2's complement = reverse the bits and add 1
-1 = 2's complement of 1
1 in 32-bit binary // 00000000000000000000000000000001
1's complement of 1 = flip all bits // 11111111111111111111111111111110
2's complement of 1 = 1's complement + 1 // 11111111111111111111111111111110 + 1
-1 // 11111111111111111111111111111111
So, what happened was,
- -1 gets converted to 2's complement.
- Shift zero number of bits to the right. That means do not shift any bits.
- Return the result as an unsigned 32-bit integer.
This always results in an unsigned integer between 0 (all 0 bits) and 0xFFFFFFFF (all 1 bits) because after >>>
operation, the 1s on the left no longer represent the signedness of number.
So is that all?
No, it's not. This is JavaScript we're talking about.
-1 >>> 0; // 4294967295 Ok, I get it
// But,
"lemons" >> 0; // 0 🤷♂️
"lemons" >>> 0; // 0 🤷♂️
undefined >> 0; // 0 🤷♂️
undefined >>> 0; // 0 🤷♂️
null >> 0; // 0 🤷♂️
null >>> 0; // 0 🤷♂️
That's JavaScript coercion gods working their magic.
When >>>
failed coercing "lemons" to a number, it resulted in 0 to ensure an integer result.
An example usage
let numbers = [1, 2, 3, 4, 5];
numbers.splice(numbers.indexOf(6), 1);
// numbers -> [1,2,3,4] --- Oops
Whenever you use splice
with indexOf()
in JS, always check if index is > 0. Otherwise if element is not found, .splice(-1, 1)
removes the last element.
The '420 byte alternative to a popular library' way to write this is,
numbers.splice(numbers.indexOf(element) >>> 0, 1);
If indexOf(element)
is -1, We know -1 >>> 0
will be 4294967295
. So splice wouldn't remove anything wrong.
Seriously, don't write code like that. Unless you are trying to save the world with 50 fewer bytes of course!
What does <<< do then?
It doesn't exist.
Use your new-found >>>
powers carefully. The best way to use is to avoid it.