I want to share a tiny character-saving trick I use when flooring a number towards zero in JS. Hopefully you’re aware of the following idiom:
~~numberToBeFloored |
Don’t worry… this post isn’t all about ~~…
In most cases this is used instead of Math.floor
because it’s faster, terser and most important, much more confusing! Be careful though, it has different behaviour to Math.floor
. For most cases, it has the effect of flooring the number towards zero. I wrote a post covering it in some detail a while ago.
It’s important to note, as pointed out by Mathias Bynens, that this and other similar bitwise operations only work reliably on numbers that can be expressed as 32-bit sequences. Any numbers above 2147483647 or below -2147483648 will not work as you expect. This is usually acceptable though.
The actual trick which I wanted to post about relates to operator precedence. I often find myself in a situation like this:
// Generate random RGB red value: var r = ~~(Math.random() * 255); |
The bitwise-not operator (`~`
) has a higher precedence than the multiplication operator (`*`
) so the parentheses around `Math.random() * 255`
are necessary to avoid the `~~`
only applying to `Math.random()`
. i.e.
// This doesn't work correctly var r = ~~Math.random() * 255; |
Fortunately, there are other similarly effective bitwise operations that have the same flooring effect, but with lower operator precedence. One of them is `0 | n`
. The pipe operator (bitwise OR) has a lower precedence than the multiplication operator so we can use it without the need for parentheses:
var r = 0 | Math.random() * 255; |
So we can save a couple of characters! Joy!
There’s also the bitwise shift operators with similarly low precedence, e.g.
var r = Math.random() * 255 >> 0; var r = Math.random() * 255 << 0; var r = Math.random() * 255 >>> 0; |
It’s definitely worth getting familiar with the various operator precedences in JavaScript. It’ll help you know when you’re wasting time with parentheses.
Mathias also gave us a JSPerf test case benchmarking all the various flooring solutions in JavaScript. Check it out!
Just to be clear, with all these strange bitwise flooring operations, we’re not interested in manipulating the bits of the 32-bit sequence at all. All we’re interested in is getting the integer produced by the internal `ToInt32`
operation, hence why all bitwise flooring techniques are no-op bitwise operations: e.g. we bitwise-not and then bitwise-not again, causing a reversal of its effects (comparable to `!!true`
). We bitwise-or with zero as an operand, meaning that for every bit-pair, the non-zero operand will win and thus nothing will change… and it’s the same deal with `n << 0`
and `n & n`
...
What's important is that accessing the internal ToInt32 via bitwise operations is much faster than via the `parseInt`
function, as is clearly evident in Mathias' test case.
... and now for the "revelation":
Regarding the usage of these tricks: over time I've come to the opinion that they're almost entirely pointless. The performance differences, even though they may seem staggering, are entirely negligible in most cases. You should only sacrifice readability for performance if you really need to squeeze out those precious microseconds. The only other reason for using these bitwise tricks is to make your code look more complicated than it really is, which is probably a stupid reason. As mentioned, there are also edge cases to watch out for. You can't just replace all your current `Math.floor()`
calls with `~~`
. Things will likely fail. What's worse is that your test coverage probably isn't exhaustive enough to catch these edge cases!
Even though I shouldn't, I still use them though. They're "cool"...
Thanks for reading! Please share your thoughts with me on Twitter. Have a great day!
I love to use bitwise operators aswell and I don’t think they decrease readabilty (at least not to any halfwhat experienced programmer I guess).
I also prefer checking for odd/even numbers with “num & 1” instead of math modulo, or switcher variables with “number ^= 1” instead of setting a boolean to true or false explicitly.
Infact, I truly believe bitwise operators used in the rights spots are very convinient and useful at the same time (the performance gain is just sugar on top)
@Andreas, I agree, they’re really useful. The flooring technique, though, is quite unconventional. I guess it’s not a typical bitwise use-case, and so it might confuse people. I’ve nothing against using bitwise in situations where bits actually need to be manipulated.
I have the same problem 🙂 It’s a double standard, really — I know I shouldn’t be using these hacks as they make the code harder to read, but I still do it whenever I can get away with it.
Some of my open-source JavaScript projects are drop-in libraries, and in those cases I make use of bitwise operators for non-bitwise use cases as I see fit. Even if it only helps performance a little bit.
In Punycode.js for example, at some point I need to check if a given BMP code point (guaranteed to be in the range from `0x0000` to `0xFFFF`) is a low surrogate (i.e. in the range from `0xDC00` to `0xDFFF`) or not. You could use the following piece of code for that:
The following has the exact same effect for all BMP code points, but is much more efficient:
It’s not as readable as the first option, but it does the job and it’s faster. Speed is more important for a drop-in library IMHO.
OTOH, for code that is developed in a team, it’s probably not a very good idea to ‘obfuscate’ the source like this in the name of performance. Sure, there might be a run-time performance gain of a few nanoseconds, but you’ll probably end up confusing your colleagues and wasting minutes of developer time.
Do you also use -~ for ceiling? Or do you have a better way of doing it?
@James @Andreas @Mathias don’t you guys feel that a comment will help your team-mates understand the operation?
Would you go with a comment + bit-wise operation or a “more readable” method/expression?
@Sujay: If it’s code you’re working on in a team, I’d prefer having a readable self-explanatory code snippet instead of a magical but more compact/efficient snippet that needs explanation. But that’s just me.
@Mathias, I guess I agree regarding drop-in libraries — it’s probably okay to use in those situations. I’m just wary of future maintainers. If a project’s team is known and all are aware of these bitwise idioms then there’s probably no problem.
@Sujay, I would prefer to use the more verbose/slower
Math.floor
instead of adding a comment for what should be such a rudimentary operation. IMO Having a comment there would just make the situation even more seemingly complex for maintainers.And
`-~`
looks cool. Not considered that before.It totally depends on the purpose of the code.
Constructs like `~~(num)` are bad, because they hide the intention of the code and might not even work as desired.
If the number to round is already referenced by a variable, it could be floored using
n - n % 1
. This might not be as readable asfloor(n)
, but it allows to get the intention.I think the algorithm should set the level, not the syntax used. Within the topic of parsing unicode and identifying surrogate pairs code like
(codePoint & 0xFC00) == 0xDC00
is totally acceptable, because the topic requires knowledge about unicode. And programmers with that knowledge are likely to have knowledge of encodings, i.e. binary representations.I’m not sure whether I got the term “switcher variable” correctly, but if it is only a condition, using
^= 1
is just a geeky try to appear smart with much opportunity for failure.The speed reason for external libraries: Why not benchmark it with
@David:
I think the programming language should dictate my “halfwhat experienced programmer” statement more. However, binary operators are/should be pretty much “above” the layer of a language, hence, if you know how they work you can apply that knowledge in any language.
The “switcher” actually IS a nice geeky thing to have a state variable beyond standard boolean values:
Of course those things should always come along with own little specs (for a team), anyways this specific example shows demonstrates how to accomplish a state-shift without booleans. Is that more readable? No, I don’t think so, but its still very understandable imo. I could also argue that someone just sets a boolean variable to “foobar hey 42 I screwed your code”, so the human factor is always the biggest opportunity for failure.
@Andreas: I agree on binary operators, but the truth is that high level scripting languages are often used by people who never had to do any „binary work“. As soon as they need to, they will hit the binary operator wall.
I get what you mean by switcher variable now. In case of toggling there is certainly a use for
^=
, although I wouldn’t use it. To bad the!=
operator does something completely different …I have to admit that I was a big friend of smart operator trickery in the past. Working in a team changed my mind. It’s better to make code as easy to grasp as possible. People usually have a better time understanding the underlying concepts and algorithms, if there is less need to explain what the code is actually doing.
In addition, today’s minifiers do a great job. I even abstain from certain JS “idioms”, because the minifier will replace longer constructs with them.
Example (with closure compiler):
Sure, every halfwhat experienced JS programmer could or should have seen the short form. But I think the longer version is easier to get. And as of today there is no longer a file size penalty. I just try to keep the cognitive demands of my code as low as I can.