A shorter way to do Math.floor – A little known trick

Nothing groundbreaking, but someone just handed me code that was filled with Math.floor calls. While this doesn’t automatically slow down your code the way it used to, thanks to better Javascript engines, it’s still ugly as hell to look at.

Now, those people who like to put wavy brackets on individual lines will probably start shouting again, but once you’ve gotten used to it, this is actually a good deal more readable, since it shortens the code considerably, while still having a “unique look” that you’ll be able to identify quickly

Instead of

You can simply do a bitwise OR with 0:

The reason this works is that any bit operation causes the number to be converted to a signed 32 bit integer. While this isn’t strictly the same as what Math.floor does, it will give you the same result at least for positive 31 bit integers. For negative ones you get something a little different (but typically far more useful), namely the integer part (-3 for -3.7) instead of the the highest integer smaller or equal to the given number (-4 for -3.7).

23 thoughts on “A shorter way to do Math.floor – A little known trick”

  1. There was a discussion on twitter the other day with @mathias and somebody else, I’ve forgotten who. There were three .floor alternatives being discussed, ~~ & and one I’ve forgotten. I don’t think it was |, but I’m not sure. Mainly because I argued that ~~ was a unary operator.
    The point was that the bitwise operator seemed to be faster, but required caching of the value. Obviously this is not the case with |, so maybe it was slower, or maybe it wasn’t one of the candidates πŸ™‚
    I’ll have to ask @mathias for the link, I can’t find it πŸ™

  2. I think this is it… at least it’s the only Math.floor related link I could find for @mathias

    ~~n; // 3
    n|n; // 3
    n&n; // 3

    But I kinda like 0|(foo)… I’ve run Math.floor against it some time ago and they were about the same. It used to be a lot faster, but current JITs probably recognize the pattern and replace it based on the range of numbers.

  3. Damn you guys are fast. Didn’t read comments #2,#3 before I posted.

  4. Just running the test on ernestdelgado.com in a current Firefox nightly, Math.floor is actually fastest.

  5. Yes, pesky optimizations ruin everything. Oh wait… :p

    The main problem with these benchmarks is that they are executed under very strict conditions, which are usually easily optimized by the interpreters. We should find a way to create a generic benchmark that would not easily be optimized that way. Then run all the hacks and tricks and see what they result in.

    I wonder what would be easier to recognize though, ~~ or 0|. As a floor I mean.

  6. A literal 0| or a ~~ would probably be about the same. The problem is that people might try to express 0| differently with variables at which point it could get complicated.

  7. Just wrote my own little benchmark and basically… the fluctuation between all of those methods is really small. With high system load (sorry, can’t do anything about it now), it’s pretty much impossible to determine what would be faster. Not even n|n is significantly slower.

  8. http://www.tapper-ware.net/devel/js/js.floorBench/

    3 runs in Firefox nightly:
    n|n:55ms per 100000, 0|n:60ms per 100000, Math.floor(n):49ms per 100000, ~~n:55ms per 100000,
    n|n:56ms per 100000, 0|n:57ms per 100000, Math.floor(n):50ms per 100000, ~~n:58ms per 100000,
    n|n:56ms per 100000, 0|n:58ms per 100000, Math.floor(n):51ms per 100000, ~~n:56ms per 100000,

    Math.floor is really the fastest, 0|n, ~~n and n|n are about the same. Can’t post any numbers for Chrome since it never finishes… apparently it’s just too slow.

  9. Interesting. On my machine (xp, 3gb, quad 2200)

    Opera 10.50/win:
    n|n:19ms per 100000, 0|n:20ms per 100000, Math.floor(n):24ms per 100000, ~~n:27ms per 100000,
    n|n:20ms per 100000, 0|n:21ms per 100000, Math.floor(n):24ms per 100000, ~~n:26ms per 100000,
    n|n:19ms per 100000, 0|n:20ms per 100000, Math.floor(n):24ms per 100000, ~~n:26ms per 100000,

    Safari 4.0.5 (531.22.7 nightly)/win:
    n|n:19ms per 100000, 0|n:17ms per 100000, Math.floor(n):22ms per 100000, ~~n:18ms per 100000,
    n|n:26ms per 100000, 0|n:17ms per 100000, Math.floor(n):21ms per 100000, ~~n:18ms per 100000,
    n|n:26ms per 100000, 0|n:17ms per 100000, Math.floor(n):22ms per 100000, ~~n:18ms per 100000,
    n|n:26ms per 100000, 0|n:17ms per 100000, Math.floor(n):21ms per 100000, ~~n:18ms per 100000,

    Firefox 3.6.3/win:
    n|n:36ms per 100000, 0|n:36ms per 100000, Math.floor(n):33ms per 100000, ~~n:38ms per 100000,
    n|n:38ms per 100000, 0|n:36ms per 100000, Math.floor(n):31ms per 100000, ~~n:37ms per 100000,
    n|n:36ms per 100000, 0|n:37ms per 100000, Math.floor(n):32ms per 100000, ~~n:36ms per 100000,

    Chrome indeed doesn’t bother to finish. IE doesn’t work because it doesn’t support the selector function.

    In conclusion, there isn’t a heckuvalot difference between any of them and it only shows when you’re using the operation _a lot_, which is not very common. In the end it comes down to a preference in typing and reading. My vote goes to ~~ πŸ™‚

  10. OK, I dumbed it down to 2 runs and removed the selectors/date functions that IE doesn’t understand. Of course with only 2 runs you get very little precision, but any more and IE throws a “interrupt script” message:

    IE8: At least we get what we expected: function calls take longer than operators.
    n|n:226ms per 100000, 0|n:257ms per 100000, Math.floor(n):328ms per 100000, ~~n:241ms per 100000,
    n|n:219ms per 100000, 0|n:226ms per 100000, Math.floor(n):491ms per 100000, ~~n:257ms per 100000,
    n|n:234ms per 100000, 0|n:218ms per 100000, Math.floor(n):327ms per 100000, ~~n:218ms per 100000,
    n|n:241ms per 100000, 0|n:226ms per 100000, Math.floor(n):717ms per 100000, ~~n:265ms per 100000,

    IE9pre: The same, but with a lot less difference
    n|n:53ms per 100000, 0|n:52ms per 100000, Math.floor(n):55ms per 100000, ~~n:52ms per 100000,
    n|n:55ms per 100000, 0|n:52ms per 100000, Math.floor(n):56ms per 100000, ~~n:52ms per 100000,
    n|n:55ms per 100000, 0|n:52ms per 100000, Math.floor(n):55ms per 100000, ~~n:51ms per 100000,
    n|n:52ms per 100000, 0|n:52ms per 100000, Math.floor(n):102ms per 100000, ~~n:58ms per 100000,

    Chrome Beta: OK, I really can’t get any useful results here, even with 10 runs:
    n|n:51ms per 100000, 0|n:38ms per 100000, Math.floor(n):53ms per 100000, ~~n:29ms per 100000,
    n|n:64ms per 100000, 0|n:30ms per 100000, Math.floor(n):72ms per 100000, ~~n:14ms per 100000,
    n|n:17ms per 100000, 0|n:77ms per 100000, Math.floor(n):54ms per 100000, ~~n:51ms per 100000,
    n|n:21ms per 100000, 0|n:31ms per 100000, Math.floor(n):91ms per 100000, ~~n:58ms per 100000,

  11. This may sound kind of stupid, but why not do an explicit cast to int? You get the equivalent to what is described here, even for negative numbers.

    Full disclosure: I put curly braces on their own line.

  12. Because there is no int type in Javascript, only double floating point numbers: Each of these methods does a double -> int32 -> double conversion, except maybe Math.floor.

  13. Ah, you’re right. I will crawl back into my Java hole now from whence I came πŸ™‚

  14. Do you really want people maintaining your code to have to figure out your bit-twiddling tricks for operations that have a built in method? I know that if I used this for speed benefits, I would add a comment to clarify what I was doing anyway. I would also be extremely surprised if profiling indicated that any particular algorithm’s bottleneck was calculating a floor.

    Cool trick though.

  15. There’s not much to figure out really. Anybody should be able to see it almost immediately (no matter which method you choose) and once you’ve seen it, I’d argue that it’s actually easier to read since you basically have something vaguely resembling an operator (visually), instead of a strange name floating among along those variables in your code. “|0” has a certain look, Math.floor doesn’t. It’s shorter AND easier to identify.

    Of course, you may comment on it the first time it appears in your code or in the accompanying documentation, but not each and every time.

  16. Sorry, while it’s a neat trick, I think code readability suffers and would probably veto this on my projects *unless* the loss in readability (aka future maintainability) were offset by worthwhile (read: “noticeable”, either casually or to an alert observer) performance gains.

    I can appreciate the performance improvements show here, but my first thought was “premature optimization.” Is that where you’re spending your time, floor-ing numbers? How many are you iterating through such that saving 10 ms per 100,000 makes a difference? Is it even noticeable to an alert observer? I can’t help but then wonder, if performance is so critical, perhaps Javascript is the wrong language or this processing is happening at the wrong layer.

    And yes, I do realize that makes me sound like an old grumbling throwback coder. Considering I cut my teeth with Vax Systems programming in C and 6502 assembler (Apple ][) in the 1970/80s I suppose that’s true to some degree. But I’ve seen plenty of clever code parlour tricks over the years, and the aftermath as someone later comes along and stumbles over the code as one would an upturned garden rake.

  17. That’s great! On Current Firefox nightlies 0| always comes out fastest, followed VERY closely by <<0 and a slightly more distant (but still close) ~~ as third.

Leave a Reply

Your email address will not be published. Required fields are marked *


This site uses Akismet to reduce spam. Learn how your comment data is processed.