Flooring a Number with Bitwise OR
Somewhere along the way, I stopped using JavaScript’s Math.floor() function and I started using bitwise OR 0 such as in the following example:
In fact, there are other ways of flooring a number by using bitwise operators. Some of the best known are:
- left shift («)
- double NOT (~~)
There are many posts out there about the relative speed of these different methods, most of which insist that Math.floor() is slower than bitwise operators. There are a few JS Perfs out there on just this topic - bitwise NOT vs Math.floor() and or-vs-floor are two of these.
But I continued to use bitwise OR 0 without really researching exactly how it worked and whether it was really better than Math.floor(). Until today.
What Happened?
I wrote a simple function that extended the Number prototype with a new method called
that returns the number as a string using English words. For example:1
toEnglish
But something happened when I tried to call this method on numbers greater than one trillion! For the number 10,000,000,000,000, instead of getting the result “ten trillion” I got something very strange.
What?
The Limitation of Bitwise Operators
I had run into the limitation of using bitwise OR. Bitwise operators treat their operands as a sequence of 32 binary bits (zeros and ones) and will actually truncate larger numbers down to 32 bits. So you really should only use bitwise operators when you know you’ll be working with 32 bit numbers. The JavaScript Number Type, however, represents the double-precision 64-bit format IEEE 754 values (for more information, see the ECMAScript Language Specification).
In the example above, my
function had a line of code using the bitwise OR 0 - 1
toEnglish
- which was dividing the num 10000000000000 by 1000 and setting num to 10000000000, which is 34 bits, before using the bitwise OR.1
num = num / 1000 | 0
Note that to quickly see the binary (base 2) number from any decimal (base 10) number, you can call the
function with a specified base as the argument. In the example, I called 1
toString()
to see the base 2 (binary) number.1
toString(2)
But How Do Bitwise Operators Work?
Bitwise operators work as follows (from MDN):
- The operands are converted into 32 bit integers and expressed as zeros and ones
- Each bit in the first operand is paired with the corresponding bit in the second operand: first bit to first bit, second bit to second bit, etc.
- The operator is applied to each pair of bits, and the result is constructed bitwise.
Below are some bitwise operators.
Bitwise AND (&)
BASE 10 | BASE 2 |
---|---|
9 | 00000000000000000000000000001001 |
14 | 00000000000000000000000000001110 |
14&9 | 00000000000000000000000000001000 |
Bitwise OR (|)
BASE 10 | BASE 2 |
---|---|
9 | 00000000000000000000000000001001 |
14 | 00000000000000000000000000001110 |
14|9 | 00000000000000000000000000001111 |
Bitwise XOR (^)
BASE 10 | BASE 2 |
---|---|
9 | 00000000000000000000000000001001 |
14 | 00000000000000000000000000001110 |
14^9 | 00000000000000000000000000000111 |
Bitwise NOT (~)
BASE 10 | BASE 2 |
---|---|
9 | 00000000000000000000000000001001 |
~9 | 11111111111111111111111111110110 |
Bitwise Left Shift («)
BASE 10 | BASE 2 |
---|---|
9 | 00000000000000000000000000001001 |
9«2 | 00000000000000000000000000100100 |
Bitwise (Sign-Propogating) Right Shift (»)
BASE 10 | BASE 2 |
---|---|
9 | 00000000000000000000000000001001 |
9»2 | 00000000000000000000000000000010 |
How Do Bitwise Operators Floor Numbers?
Bitwise operators floor numbers simply because they convert their operands into 32 bit integers. This conversion to integer, however, is done by truncation and not by rounding the number. This means that for negative numbers, the results of using Math.floor() won’t be the same as the results of using a bitwise operator. Take a look at the following:
There’s also a difference with NaN:
Bottom Line
Using these bitwise operators isn’t the equivalent to using Math.floor(), though for positive numbers that have fewer than 32 bits the results will be the same. To be on the safe side, however, it’s a good idea to know what your data is and to be consistent.