Thursday, June 05, 2008

Two simple tricks in JavaScript ( olds, but always useful )

This is a quick post about two really common pieces of code that are used daily from libraries developers and not.

Stop to use Math.floor


The first one is about usage of Math.floor. It is probably only my opinion, but it seems that Math.floor is used always to perform the same task:

var centerWidth = Math.floor((something + someelse - someother) / 2);

The point is that at the end of a Math.floor, you will often find that division by 2.
There is a truly simple way to write less, and to obtain best performances as well, it is the right side bitwise operator, that for this purpose is nearly perfect.

var centerWidth = (something + someelse - someother) >> 1;

That's it! If you compare above examples you will note that second one is about 2X faster than Math.floor.

for(var i = 0, t1 = new Date; i < 50000; i++)
Math.floor(i / 2);
t1 = new Date - t1;

for(var i = 0, t2 = new Date; i < 50000; i++)
i >> 1;
t2 = new Date - t2;

alert([t1, t2].join("\n"));


A tricky String.indexOf


Another common piece of code you can find wherever, is this one:

if(myWord.indexOf(myChar) >= 0) ...

if(-1 < myWord.indexOf(myChar)) ...

if(myWord.indexOf(myChar) !== -1) ...

There is an operator that inverts the number, adding +1, and that is perfect every time we have a function that could return 0, more than zero, or -1 where there's no match.

if(~myWord.indexOf(myChar)) ...

Above example converts every number from 0 to N into -1 to -(N+1)
Accordingly, if the result is -1, the result will be zero -(-1+1), where -0 does not make sense in JavaScript, and it is simply threaded as 0 (no limits guys :D)

The latter one is probably not known as the first one, and its execution time is about the same, but you write less, and you can recognize instantly if that method or function respect the integer -1 to +N return value.
The implicit cast when you use an if or a ternary operator, is the one that makes that check fast enough, but not faster than common way as is the first suggested trick.

P.S. these tricks could be used with many other program languages ;)

9 comments:

kangax said...

Interesting.

I always considered bitwise operators slow, but your testcase proves it wrong.

Gareth Heyes said...

Yep great stuff!
More please :)

gregof said...

Be careful using big numbers!
2147483648 >> 1 //-1073741824

Andrea Giammarchi said...

gregof, your example does not make sense ... that is the tipical Math.pow(2, 31) limit and does not mean that we have to be careful with >> operator.

First of all, because I suppose there are rare cases where we use such big integers with JavaScript, secondly, because your comment is, basically, like this one:

be careful with Arrays length:

var a = new Array(Math.pow(2, 32));
a.push(123);// Error, invalid array length
a.lengt++; // Error, invalid array length


Does it mean that we canot use arrays? :roll:

Mega69 said...

Great!

I also think we can totally replace Math.floor with:

number>>0.5

for example:

19.46543 >> 0.5

returns 19

Andrea Giammarchi said...

Yes Mega, I don't know why I did not think to write an example with num >> .5 ;)

gregof said...

Andrea Giammarchi, you can use arrays :)
Not all readers of your blog know about this limit for using binary operations.
They have to be know that use "i>>1" in place of "Math.floor(i/2)" possible not always.

Andrea Giammarchi said...

I think in daily real applications, it is always possible.
Anyway, I will talk about binaries and numeric JS limits, in another post :)

Luke Smith said...

The bitwise operators are only equivalent to Math.floor for n >= 0

Math.floor(-10.7); // -11
-10.7 >> 0.5; // -10
-10.7 | 0; // -10

Also, imo they tend to obscure the code for future maintainers. I can't think of any operations in js that would be performing the amount of Math calls needed to yield a noticeable degradation in user experience.

Incidentally, in some benchmarks I did a while back, I found that storing the global Math (or a specific Math function) in a local or closure var alleviates most/all of the performance penalty.

That said, I certainly appreciate the tips! It's always good to see clever code patterns, regardless if you'll use them. I'd played with the ~ for a while, but didn't find a use for it. I'll probably stick to stored Math functions and simple comparators for the benefit of those who inherit my code... but maybe the ~index thing will slip into a personal project or two :)