Flooring a Number

May 06, 2014

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:

2.145 | 0  // 2

In fact, there are other ways of flooring a number by using bitwise operators. Some of the best known are:

  • left shift («)
2.145 << 0  // 2
  • double NOT (~~)
~~2.145  // 2

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

1
toEnglish
that returns the number as a string using English words. For example:

> (123456789).toEnglish()
  "one hundred twenty-three million four hundred fifty-six thousand seven hundred eighty-nine"
> (1000000000000).toEnglish()
  "one trillion"

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.

> (10000000000000).toEnglish()
  "one trillion four hundred ten billion sixty-five million four hundred eight thousand"

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

1
toEnglish
function had a line of code using the bitwise OR 0 -
1
num = num / 1000 | 0
- which was dividing the num 10000000000000 by 1000 and setting num to 10000000000, which is 34 bits, before using the bitwise OR.

> (10000000000).toString(2)
  "1001010100000010111110010000000000"

Note that to quickly see the binary (base 2) number from any decimal (base 10) number, you can call the

1
toString()
function with a specified base as the argument. In the example, I called
1
toString(2)
to see the base 2 (binary) number.

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:

> Math.floor(2.134)
  2
> 2.134 | 0
  2
> Math.floor(-2.134)
  -3
> -2.134 | 0
  -2

There’s also a difference with NaN:

> Math.floor(NaN)
  NaN
> NaN | 0
  0

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.

Freaq Out!

## The `freaq` bookmarkletOne of my students, Zach Pomerantz, created a bookmarklet called [freaq](http://www.freaq.io/) that is an audio...… Continue reading

Bitwise N-Queens

Published on May 08, 2014

Prime Sieves

Published on April 26, 2014