'>>>' operator in JavaScript

August 19, 20204 min read

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.

References

Stackoverflow

MDN

ExploringJS