Who would have thought that JavaScript’s rare and mysterious bitwise operators could be so useful? I first discovered this trick a while ago from a slideshow on JavaScript performance, by Thomas Fuchs. The “trick” is as follows:
~~something; |
The bitwise NOT operator (~
) will take its operand, convert it to a 32-bit integer, and will invert each bit so that each 0
becomes a 1
and vice versa.
00000000000000000000000000001001 ...becomes 11111111111111111111111111110110 |
The effect of this, given the expression ~foo
is -(foo + 1)
. A double bitwise NOT operation (~~foo
) will therefore result in -(-(foo + 1) + 1)
. This only remains true for integers though; given all the potential operands, the real equivalent expression to ~~
is probably something like:
typeof foo === 'number' && !isNaN(foo) && foo !== Infinity ? foo > 0 ? Math.floor(foo) : Math.ceil(foo) : 0; // This is ONLY _effectively_ the same... this is // NOT what happens internally! |
This is obviously a fair bit slower than ~~
.
If the operand is a number and it’s not NaN
or Infinity
then ~~
will have the effect of rounding it towards zero (Math.ceil
for negative, Math.floor
for positive). If it’s not a number, then I believe the internal ToInt32
function converts it to zero.
Here are some examples of the double-bitwise NOT operation in all its glory:
~~null; // => 0 ~~undefined; // => 0 ~~0; // => 0 ~~{}; // => 0 ~~[]; // => 0 ~~(1/0); // => 0 ~~false; // => 0 ~~true; // => 1 ~~1.2543; // => 1 ~~4.9; // => 4 ~~(-2.999); // => -2 |
~~
‘s flooring capabilities make it a better alternative to Math.floor
if you know you’re dealing with positives — it’s faster and takes up less characters. It’s not quite as readable though, but I’m hoping that ~~
will slowly become a very well-known technique in the JS arena, so we can all use it without fearing accusations.
It’s quite useful for normalizing arguments that you expect to be integers too. Take this example from what the MDC recommends for providing Array.prototype.indexOf
to non-supporting browsers:
/*Array.prototype.indexOf = function...*/ var from = Number(arguments[1]) || 0; from = (from < 0) ? Math.ceil(from) : Math.floor(from); |
In goes ~~
:
/*Array.prototype.indexOf = function...*/ var from = ~~arguments[1]; |
Go forth and double-NOT!
Thanks for reading! Please share your thoughts with me on Twitter. Have a great day!
I always used bitwise left shift (by 0 bits) to do that (x << 0).
Interesting alternative.
Nice post, but I wouldn’t get my hopes up if I were you. Bitwise operators are present in many programming languages. That, however, does not mean people use them a lot. That is; there sure is a use for them, but overall you should worry about your code being clean or not, not how “cool” it looks.
This post remembers me of https://j11y.io/javascript/anti-patterns-in-the-making/
Another more useful trick is the double NOT :
var bolean = !!Anything;
will convert anything into a bolean value.
Personally I think ~~ is rather an anti-pattern that should be used sparingly. The problem is that this obfuscates your code. The true logic being done here is hidden behind two chars. We all know, that eval could be useful sometimes although it is evil.
As an coincidence I wrote a blog post last weak about the single ~ operator:
http://kambfhase.blogspot.com/2010/02/operator.html
mfG Kambfhase
Reminded me of Perl.
Yes, I quite like using !! to return a boolean, and it’s a lot more readable than ~~, because it’s a lot less confusing than bitwise operators. !! === “not not” which is pretty easy to understand, whereas bitwise operators are certainly a lot more complex and require an understanding of the binary numeral system.
I, for one, don’t really come across many use-cases for bitwise operators, especially in JavaScript.
@James, why are you comparing
!!
to~~
? — they do very different things.I don’t come across many use-cases for bitwise operators either; that’s why I’m so excited when I find a useful way to use one of them…
@kambfhase,
eval
is a totally different story — it doesn’t obfuscate, it’s a bad practice for a very different set of reasons. Also, I think~~
‘s (un)?readability is highly dependent on the person reading the code — if I saw it, I’d know what’s going on, so would you, and other people that have come across it before.One of the best use-cases for bitwise operators is when doing Enum ‘concattenations’
var Option1 = 2
var Option2 = 4
var Option3 = 8
var Option4 = 16
…
var Option10 = 1024
var SelectedOptions = Option3 & Option6 & Option10; //= 1000100100
which can be passed as a single parameter
Then you’re able to check the options later on by doing:
if(Option6 & SelectedOptions)
{
doSomething()
}
I know !! and ~~ do very different things. They are both examples of operators that can be used as shortcuts if used twice, but involve very different levels of complexity.
Thanks Martin, I would have used an object or an array, so I guess I just don’t really understand the uses of bitwise operators and using numbers to their full advantage.
From doing some simple tests, it seems that bitwise operations are blazing in IE8 vs FF3.6 / Chrome 4. Anyone know why this is? Try it for yourself.
I’ve spotted the deliberate mistake! 🙂 In your examples near the end:
“~~(-2.999); // => 2”
should be:
“~~(-2.999); // => -2”
Shouldn’t @Martin Kirk example be:
Great article, thanks to Martin for his additional pieces of code too.
@Carlos
what’s up with the & ???
the bitwise operator is ‘&’
@Carlos
sorry… the HTML formatting has been fixed
@Martin Kirk My question remains, shouldn’t this line
var SelectedOptions = Option3 & Option6 & Option10; //= 1000100100
be
var SelectedOptions = Option3 | Option6 | Option10; //= 1000100100
@Carlos
ahhh — yes you are right !
Or you could add 0.5 and have Math.round() and by adding 1 you’d have Math.ceil();
Math.floor(x) == ~~(x)
Math.round(x) == ~~(x + 0.5)
Math.ceil(x) == ~~(x + 1)
Cheers,
Tom
Note that bitwise operators convert numbers to a 32-bit sequence before processing them, so the range is different.
Alternatives to
Math.floor
using bitwise operators will only work with positive signed 32-bit floats, i.e. numbers from 0 to +2,147,483,647 (2^31-1).I’ve created a jsPerf test case to benchmark these different approaches. Feel free to add more possibilities!
~~ is going to work as Math.floor for negative numbers.
~~(-2.999) = -2
Math.floor(-2.999) = -3
I meant not going to work. (typo)