DEV Community

Gonçalo Morais
Gonçalo Morais

Posted on • Originally published at blog.gnclmorais.com on

Binary in JavaScript

This article is based on a lightning talk I recently did at dotJS and it was written for Mariko’s Web Advent Calendar. Check all the other interesting articles, specially Pam’s and Ricardo’s!


I’m not entirely sure about how many web developers know about (or even use) it, but JavaScript is capable of binary. 0s and 1s can easily be manipulated with bitwise operators on our favourite language and that’s what I’ll present on this post.

First of all, why? Why would you care about this? In your years of web development, you probably never had the need to use any of these operations, so why are you even reading this? OMG is it one more thing to know and add to my JavaScript fatigue??

Don’t worry, this is just a piece of curiosity. Please keep reading if you love quirks! This article will be a brief introduction to the available bitwise operations, but I can already recommend you a great post from Dan Prince. In short, he was able to greatly reduce the memory footprint of a game we was developing using bitwise operators. He was working on a 512x512 pixel matrix, using Plain Old JavaScript Objects to represent each pixel. However, using just the strictly necessary bits to save the game’s state, each object was replaced by an integer, reducing the memory consumption four times! You’ll find more info in his blog post.

A few technicalities first

Let me quickly tell you a few important technical details about how JavaScript deals with numbers and binary operators.

Numbers are stored using 64 bits

Basically, all numbers in JavaScript are floating point. A single bit for sign (0 for positive and 1 for negative numbers), 11 bits exponent bits to indicate where the point is, and finally 52 bits representing the actual digits of the number.

   sign | exponent | fraction
(1 bit) | (11 bit) | (52 bit)
     63 | 62 -- 52 | 51 --- 0

Enter fullscreen mode Exit fullscreen mode

Numbers with more than 32 bits get truncated

It means that, from the 64 bits you read on the previous paragraph, we’ll only keep the 32 at the right (i.e. the least significant).

                                   // 15872588537857
Before: 11100110111110100000000000000110000000000001
After: 10100000000000000110000000000001
                                   // 2684379137

var a = (-5 >>> 0).toString(2);
// "11111111111111111111111111111011"
parseInt(a, 2);
// 4294967291

Enter fullscreen mode Exit fullscreen mode

Bitwise operations are performed on pairs of bits

Operations are performed by pairing up each bit in the first operand with the corresponding bit in the second operand. Example:

// Using only eight bits here for illustration purposes:
var a = 9; // 0000 1001
var b = 5; // 0000 0101

a & b -> a // 0000 1001
              &&&& &&&&
         b // 0000 0101
              ---------
              0000 0001 -> 1 (base 10)

Enter fullscreen mode Exit fullscreen mode

Bitwise operators

JavaScript has seven bitwise operators, all of them convert their operands to 32-bit numbers.

& (AND)

| a | b | a & b |
|---|---|-------|
| 0 | 0 | 0 |
| 0 | 1 | 0 | 
| 1 | 0 | 0 |
| 1 | 1 | 1 |

Enter fullscreen mode Exit fullscreen mode

Simply speaking, & results in 0 if there is at least one 0.

| (OR)

| a | b | a | b |
|---|---|-------|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 1 |

Enter fullscreen mode Exit fullscreen mode

In the case of |, the output will be 1 if there is at least one 1.

^ (XOR)

| a | b | a ^ b |
|---|---|-------|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |

Enter fullscreen mode Exit fullscreen mode

Different bits will result in 1, simply put. I must admit XOR is my favourite, it can be quite baffling. 10 points to whoever knows what the following code does:

var a = 1, b = 2;
a ^= b; b ^= a; a ^= b; // wat?

Enter fullscreen mode Exit fullscreen mode

If you didn’t get it, don’t worry, you are not alone. It’s a very obfuscated value swap without a third variable (only between integers, though). Check this out:

var a = 1; // 0001
var b = 2; // 0010

a ^= b; // 0001 ^ 0010 = 0011
b ^= a; // 0010 ^ 0011 = 0001
a ^= b; // 0011 ^ 0001 = 0010

console.log(a); // 2 (0010)
console.log(b); // 1 (0001)

Enter fullscreen mode Exit fullscreen mode

Mind. Blown.

~ (NOT)

NOT operator simply inverts all the bits, including the sign. It’s like inverting the colours of an image.

 9 = 00000000000000000000000000001001
     --------------------------------
~9 = 11111111111111111111111111110110 = -10 (base 10)

Enter fullscreen mode Exit fullscreen mode

Applying ~ on any number x results on -(x + 1). In the example above, ~9 yields -10. This is related to the way JavaScript represents 32-bit numbers using two’s complement (something that we will not get into detail here).

<< (left shift)

<< pushes 0-bits from the right towards the left , droping as many from its left as the ones pushed from its right.

9 : 0000 0000 1001
9 << 2 : 0000 0010 0100 // 36
                     ^^
                     new bits

Enter fullscreen mode Exit fullscreen mode

>> (Sign-propagating) right shift

>> shifts bits towards the right, but it is not simply called right shift because unlike the left shift, it doesn’t push always zeros. The bit pushed depends on the sign of the number: if the number is positive, 0-bits will be pushed; if the number is negative, 1-bits will be used instead.

 9 : 0000 0000 1001
 9 >> 2 : 0000 0000 0010 // 2
          ^^
          new bits

-9 : 1111 1111 0111
-9 >> 2 : 1111 1111 1101
          ^^
          new bits

Enter fullscreen mode Exit fullscreen mode

>>> (Zero-fill) right shift

>>> is a specific case of right shift, where the new bits coming from the left towards the right are always 0, independent of the sign of the number. A consequence of it is that it turns any negative number into positive.

 9 : 0000 0000 1001
 9 >>> 2 : 0000 0000 0010
           ^^
           new bits

-9 : 1111 1111 0111
-9 >>> 2 : 0011 1111 1101
           ^^
           new bits

Enter fullscreen mode Exit fullscreen mode

Fun with bitwise operators

So what can we do with these operators? Given their quirks and behaviour, let’s see some weirdness in action. A lot of these quirks stem from the transformation from 64-bit to 32-bit.

Truncate numbers

var a = 3.14;
var b = -3.14;
console.log(a & a, b & b); // 3, -3
console.log(a | 0, b | 0); // 3, -3
console.log( ~~a,~~ b); // 3, -3

Enter fullscreen mode Exit fullscreen mode

Convert strings to numbers, emulating parseInt

var a = '15' >>> 0;
var b = '15.4' >>> 0;
console.log(a, b); // 15, 15

var c = '3.14';
var d = c | 0;
var e = c & c;
console.log(d, e); // 3, 3

Enter fullscreen mode Exit fullscreen mode

Multiply a number by multiples of 2

console.log(7 << 1); // 7 * 2 * 1 = 14
console.log(7 << 2); // 7 * 2 * 2 = 28
console.log(7 << 3); // 7 * 2 * 3 = 56
// …

Enter fullscreen mode Exit fullscreen mode

Different sub-string search

var string = 'javacript';
var substr = 'java';

// If the sub-string is found,
// appying NOT to the index will return a negative number,
// which is a truthy value;
// If not found, `indexOf` will return -1,
// which in turn ~(-1) == 0, into the `else` case.
if (~string.indexOf(substr)) {
  // Found the sub-string!
} else {
  // Nope, no match
}

Enter fullscreen mode Exit fullscreen mode

So… should you use this?

Short answer… no.

Long answer… it depends. As you’ve seen, there are a lot of gotchas and quirks people need to be aware of when using this. You need to know the variable types you’re dealing with, and that’s hard(er) to do in a dynamically typed language like JavaScript. You wouldn’t want to accidentally truncate numbers with decimals or make a negative number positive.

Other issue you should have in consideration is the consequent code obfuscation when you decide to write x << 1 instead or x * 2, for example. However, this might be a compromise you are willing to do, which becomes pretty manageable with wrappers like tiny-binary-format.

Finally, keep in mind that Douglas Crockford doesn’t like it, considering it one of the bad parts of JavaScript.

However , for side projects or applications where you need to squeeze more out of the hardware you are working on, why not? I write JavaScript for fun on my personal projects, and in those cases I like to do different things than I do on my daily job. If that involves shifting bits left & right, good for you! Keep your code weird and interesting — and learn something along the way.

Top comments (0)