Question

I understand that ~ is a bitwise NOT operator, but how does inverting the bits on a number twice make it function as Math.floor What does ~~ ("double tilde") do in Javascript? describes the differences between using Math.floor vs bitwise operations to round numbers in Javascript, but I am interested in how exactly inverting the bits twice accomplishes this.

Thanks

Was it helpful?

Solution 2

From the spec, Bitwise NOT, ~

  1. Let expr be the result of evaluating UnaryExpression.
  2. Let oldValue be ToInt32(GetValue(expr)).
  3. Return the result of applying bitwise complement to oldValue. The result is a signed 32-bit integer.

Definition of ToInt32 here.

The "complement" of a 32-bit integer i is i XOR 0xFFFFFFFF.

So put this all together and you have ~~i as meaning

ToInt32(i) XOR 0xFFFFFFFF XOR 0xFFFFFFFF
// same as 
ToInt32(i) XOR 0x00000000
// same as
ToInt32(i) 

Keep in mind the differences in rounding direction for negative numbers.


Personally I prefer using x | 0 over ~~x because it involves fewer operations for the same result.

OTHER TIPS

~ forces the operand to get cast to an integer. It's a trick to work around the fact that Javascript has no direct way of casting between floating point and integer values. In fact, Javascript doesn't officially have an integer-only type, but implementations are likely to use integers internally for performance.

So ~x is the bitwise inverse of castToInt(x). Then you inverse the bits again to get just castToInt(x).

It happens that casting floating point numbers to integer behaves almost like Math.floor. The most notable difference is that casting rounds towards zero, while Math.floor rounds towards negative infinity. There are some other differences as well:

js> ~~(-4.5)
-4
js> Math.floor(-4.5)
-5
js> ~~Infinity
0
js> Math.floor(Infinity)
Infinity
js> ~~NaN
0
js> Math.floor(NaN)
NaN
js> Math.floor(1e12)
1000000000000
js> ~~1e12 
-727379968      // <- be aware of integer overflows!

It's essentially the equivalent of a truncate function (in the sense that it is casting the float into an integer, which does exactly that), which JavaScript does not have. This is why for negative numbers the behavior is actually closer to Math.ceil.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top