JavaScript is a quite magical—and definitely wondrous—programming language, when you look at the Abstract Equality Comparison Algorithm, type coercion (yes, I’m looking at you `!+[]+[]+![]`

), and prototype pollution (both the dangerous and the ugly ways). 🧙♀️

Sooo, let’s say you want to round a number in JavaScript, specifically *floor* it. What do you do? It’s pretty straight forward.

`Math.floor(number);`

If we pick ** 25.96** for our

`number`

, the code above will return `25`

. But `Math.floor(number)`

is long, especially when you chain it with other functions. As someone who likes to use language features like the PHP spaceship operator to solve problems.Therefore, let’s dive into other alternatives JavaScript has up it’s sleeve for flooring decimals. First though, I want to spoil you by saying that `~~number`

is the shortest way to floor a number in JavaScript.

Why does `~~number`

even work? And how so? Should we use it? Let’s explore! 🧭

## String conversion

In JavaScript we can use `Number#toString`

to convert our number to a string. Strings can be `split`

by the decimal point. And then we can access the first array entry, and we get our 25.

`+number.toString().split(".")[0];`

Now this is quite a creative way of using string conversion. And it’s even longer than `Math.floor(number)`

. It has even more disadvantages, most notably, converting the number to a string. That is in and of itself an expensive operation.

## Parsing it as an int

JavaScript wouldn’t be JavaScript with little global functions that only a few people really understand. `parseInt`

is one of them. It works a bit like `(int)`

casting in Java and C++.

`parseInt(number);`

Now this is good, and shorter than `Math.floor`

. However, it is intended for strings and cannot be used in functional methods like `filter`

, `map`

, and `reduce`

. Moreover, as detailed in this stackoverflow answer, `parseInt`

also accepts “trailing characters that don't correspond with any digit”.

## Type Coercion to the resuce!

Now we’re getting to the fun stuff. JavaScript is not strictly typed. You can round, for example, a string or an object and the function will return `NaN`

, “**N**ot-**a**-**N**umber”. But that also means that binary operators, such as binary AND (`a & b`

) and XOR (`a ^ b`

), can be used with types that are not a number, including the `NaN`

constant.

There is a catch with using floating point numbers, so non-integers, for binary operations. It will either break your numbers or... coerce them to integers?

Yes, that’s exactly what the double-tilde “operator” does. `~~number`

for our example `25.95`

will evaluate to `25`

. The reason why it does is interesting. JavaScript will execute `ToInt32`

`(number)`

internally, leading to the number being converted to a 32-bit signed integer. In fact, all the following operators will convert `number`

to an `Int32`

in their calculations.

`~number`

: binary NOT, returns`-26`

`number << 0`

: left shift, returns`25`

`number >> 0`

: right shift, returns`25`

And the following operators will convert `A @ B`

—with `@`

being the operator and `A`

and `B`

being numbers—likewise to 32-bit signed integers. See the specification.

`number ^ 0`

: binary XOR, returns`25`

`number & number`

: binary AND, returns`25`

`number | 0`

: binary OR, returns`25`

## The Double Tilde

As we’ve just learned, `~number`

will convert our number to a 32-bit signed integer. Using `~~number`

will reverse the **binary NOT** operation and leave us with `25`

. Great! 🎉

You might want to point out concerns with maintainability. And you are absolutely right, let’s hold off on that though. There is a technical reason why you should never use the double-tilde to floor a number. It fails the overflow-test.

Let’s construct a reasonable example.

You have a business banking system which allows customers to create transactions. You know that using decimals is bad for currencies, so you use integers that count the amount of money in cent. Now, you want to floor the transaction amount to avoid other problems. That is why you use the double-tilde to round the incoming amount.

One day, a customer wants to transfer `4284967296`

cent ($42,849,672.96) from their account to an account at another bank. You do your checks and then floor the amount with the double-tilde operator. `~~4284967296`

. Surprisingly, the balance of the customer’s account just increased by $10,000.

## Wait... What? 🤔

The conversion to a 32-bit signed integer with `ToInt32(number)`

not only involves the `floor`

ing but also a calculation of *modulo* 2^{32} on the floored number. I'm not sure why this is done but my educated guess is that it is to avoid overflowing the 32-bit integer internally.

`4284967296 % (2 ** 32) => -1000000`

And now you have to explain your boss how using two tilde characters in your bank’s software calculation caused a customer to get $10,000 from you for free. If they had just read the documentation.

## Maintainability

After learning that the answer to “Should we use it?” is a straightforward **HELL NO**, let’s just quickly consider the maintainability aspect of using an undocumented language feature.

If JavaScript had a overflow-safe floor operator, using it in a professional environment would sound good to me. One could argue that it wouldn't be good because `Math.floor(number)`

expresses a clearer intent. And yes, probably if you have never used the operator before.

Using an undocumented *hack* that nobody understands—in the presence of a proper solution!—will make your code less understandable.

## Conclusion

A few years ago, I got a double-tilde flooring through a code review but the reviewer asked me to comment what it does it because I argued it was more readable. Oh boy, was I wrong. As far as I know, this code is still out in the wild, although I might’ve changed it to a proper `Math.floor`

at some point... Luckily, I’ve never gotten the chance to implement this at a bank before finding out about it’s danger.

I leave you with this: don’t ever use the double-tilde “operator” for flooring a number. It’s bad at best and dangerous at worse. 🙅♀️