JavaScript: fast floor of numeric values using the tilde operator

OK, put this one in the completely weird bucket. It works, but damn it’s obscure.

tildephoto © 2010 Fling Poo | more info (via: Wylio)The tilde operator (~) is one of those bitwise operators that can be useful, but that aren’t used in general JavaScript programming. Put baldly, it inverts the bits of a 32-bit value. Bitwise NOT in other words.

OK, a step back. JavaScript doesn’t have 32-bit values; its number type is an IEEE double floating point value. There is an internal ‘abstract’ routine that’s known as ToInt32. ECMAScript v5 (pdf) defines it in clause 9.5:

  1. Let number be the result of calling ToNumber on the input argument.
  2. If number is NaN, +0, −0, +∞, or −∞, return +0.
  3. Let posInt be sign(number) * floor(abs(number)).
  4. Let int32bit be posInt modulo 232.
  5. If int32bit is greater than or equal to 231, return int32bit − 232, otherwise return int32bit.

So, in essence it takes a floating point number value, calculates its floor as a 32-bit signed integer. There’s a big problem with it though: it’s internal and we can’t ever access it directly. It’s also ‘abstract’, which means it doesn’t exist per se, the interpreter/compiler is free to apply any old optimization it needs to. We can only use its behavior when we use some operator that works on 32-bit integers: JavaScript will make the conversion for us, do the operation on the 32-bit operator, and then convert the answer back to a number variable. The operators I’m talking about here are the bitwise operators: << (left shift), >> (right shift with sign extension), >>> (right shift with zero fill), & (bitwise AND), | (bitwise OR), ^ (bitwise XOR) and our new friend ~ (bitwise NOT).

The nice thing about bitwise NOT is that it’s unary and, if you apply it twice, you get the same 32-bit integer you started with. (I think you’re beginning to see where this is going…)

OK. let’s suppose we write a dice-throwing function. It’s got to return an integer between 1 and 6. A first cut might look like this:

var throwDie = function () {
  return Math.floor(Math.random() * 6) + 1;
};

Nice and obvious: Math.random() returns a value between 0.0 (inclusive) and 1.0 (exclusive), we multiply by 6 (0.0 <= value < 6.0), take the floor (integer, 0 <= value < 6), and add 1.

However, using our new knowledge we can write:

var throwDie = function () {
  return ~~(Math.random() * 6) + 1;
};

The first application of the tilde operator takes the floor for us and NOTs it (well, to be pedantic, it applies the abstract function above to get a floored 32-bit value and then NOTs that), so we apply the tilde operator again to undo the NOT. Double tilde stands in for Math.floor(), providing that we’re talking about numbers in the 32-bit signed integer range.

Less typing, but, boy, is it obscure!

Album cover for Atomic CityNow playing:
Johnson, Holly - Beat the System
(from Atomic City)



Posts on similar topics...

1 Response

  • Thu 28 Jul 2011
  • 5:06 AM
  •  avatar #1

Chilla42o said...

The tilde operator applied to arrays makes a fast and easy inArray-check:

Single tilde:

~['apple','banana','ananas'].indexOf('banana'); // = -2 (negative index starting with -1, 0 if not found)

~['apple','banana','ananas'].indexOf('orange'); // = 0 / not found

Double tilde:

~~['apple','banana','ananas'].indexOf('banana'); // = 1 (positive index starting with 0, -1 if not found)

~~['apple','banana','ananas'].indexOf('orange'); // = -1 / not found

Leave a Response

Some MarkDown is allowed, but HTML is not. (Click to learn more.)

  • Emphasize with italics: surround word with underscores _emphasis_
  • Emphasize strongly: surround word with double-asterisks **strong**
  • Link: surround text with square brackets, url with parentheses [text](url)
  • Inline code: surround text with backticks `IEnumerable`
  • Unordered list: start each line with an asterisk, space * an item
  • Ordered list: start each line with a digit, period, space 1. an item
  • Insert code block: start each line with four spaces
  • Insert blockquote: start each line with right-angle-bracket, space > Now is the time...

Extras

Search

About Me

I'm Julian M Bucknall, the M because it's my middle initial and because I and the other Julian Bucknall (the movie guy) would like to differentiate ourselves.

I'm a programmer by trade, an actor by ambition, and an algorithms guy by osmosis. I write articles for PCPlus in my spare time, not that there's much of that.

Julian M Bucknall Apart from that, an ex-pat Brit, atheist, microbrew enthusiast, Pet Shop Boys fanboy, slide rule and HP calculator collector, amateur photographer, Altoids muncher.

DevExpress

I'm Chief Technology Officer at Developer Express, a software company that writes some great controls and tools for .NET and Delphi. I'm responsible for the technology oversight and vision of the company.

Validation

Validate markup as HTML5 (beta)     Validate CSS

Bottom swirl

Archives

May 2012 (4)
SMTWTFS
« Apr  
12345
6789101112
13141516171819
20212223242526
2728293031

Like this Archive Calendar widget? Download it here.

Social networking

The OUT Campaign

The OUT Campaign

My Tweets

  • Honest Movie Trailer of Phantom Menace http://t.co/sif8y4Ns and then Battleship, er, Transformers http://t.co/sif8y4Ns
  • Damn, Donna Summer and Chuck Brown both gone in the last 24 hours. Different types of music, sure, but enjoyed them both. :(
  • Just saw a company page showing a list of tweets with "Join the conversation" linked to their Twitter a/c. The tweets are 6 months old #fail
Bottom swirl