Bitwise NOT in Javascript

isNumber Function

Even if I used some best-ever-practice in one of my projects, it doesn’t mean I can’t find even-better-approach in the next one. In other words there is always a place for learning.

Let’s take a look into simple function named isNumber

function isNumber(n){
     return typeof n === “number” && !isNaN(n) && n !== Infinity;
}

(to be honest, not the best approach, but it does its job)

Now, let’s try to improve/optimize this method by using bitwise not operation (~). For those who not familiar with such operation: ~ (bitwise not) operator will take its operand, convert it 32-bit integer and will invert each bit.

00000000000000000000000100111010


becomes

11111111111111111111111011000101


The same effect could be achieved by the following formula: – ( foo + 1 ). So, if ~foo = – ( foo + 1 ), then double bitwise operation will lead to ~~foo = – ( – ( foo + 1 ) + 1 ) = – ( –foo – 1 + 1 ) = foo. But please remember that foo will be converted to 32-bit integer internally.

From the spec:

image

image

As you can see javascript engine will do all the checks internally on stage 2, in method ToInt32.

And what will happen if operand is not an integer at all? Please note that stage 1 in method ToInt32 says: “Let number be the result of calling ToNumber on the input argument” and here is a table of To Number Conversions:

Argument Type Result
Undefined NaN
Null +0
Boolean The result is 1 if the argument is true. The result is +0 if the argument is
false
Number The result equals the input argument (no conversion).
String See grammar and note below.
Object Apply the following steps:
1. Let primValue be ToPrimitive(input argument, hint Number).
2. Return ToNumber(primValue).

Please note that only boolean argument equal to true, can lead to incorrect results. So let’s rewrite our isNumber function with using double bitwise NOT operation:

function isNumber(v){
    return v === 0 || (!!~~v && v !== true);
}

Let’s see how it works

isNumber(null)           //false 
isNumber(undefined)   //false 
isNumber(0)              //true 
isNumber({})            //false 
isNumber([])             //false 
isNumber(Infinity)      //false 
isNumber(false)         //false 
isNumber(true)          //false 
isNumber(1.2345)      //true 
isNumber(2.5)           //true 
isNumber(-2.5)          //true

Math.floor performance revising

Another interesting observation is that double bitwise NOT behaves as Math.floor

~~2.5     //2
~~2.1     //2
~~(-2.5) //-2

This leads to another ways of double bitwise NOT usage:

1. You want to convert the number from float to integer.
2. You want to perform same operation as Math.floor() but a bit faster.
3. You want to minimalize your code.

string.indexOf optimization

Another good example of using bitwise NOT was found by me in JQuery source code, take a look:

if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) { 
    setClass += classNames[ c ] + " "; 
}

This code do the same job as:

if ( setClass.indexOf( " " + classNames[ c ] + " " )  == -1) { 
    setClass += classNames[ c ] + " "; 
}

It is up to you to decide what approach is more readable or effective, but I like the idea.

Bitmasks

And of course you’re aware of bitmasks, where NOT operation sometimes is irreplaceable. I won’t cover it here, but if you’re interested you can get some examples on MDN.

If you have any other good usages of bitwise NOT operation (excluding its direct usage in computation) – please let me know!

 

UPDATE: @larrybattle, one of the commenters, pointed me to the drawback of the selected approach – it fails to validate numbers written by exponent notation:

isNumber( 10e-3 )  === false

Also he noted a good example of isNumber function in JQuery library:

function isNumber(n){
     return !isNaN( parseFloat( n ) );
}

Good job @larrybattle!