Javascript represents Number
as Double Precision 64-bit Floating numbers.
Math.floor
works with this in mind.
Bitwise operations work in 32bit signed integers. 32bit signed integers use first bit as negative signifier and the other 31 bits are the number. Because of this, the min and max number allowed 32bit signed numbers are -2,147,483,648 and 2147483647 (0x7FFFFFFFF), respectively.
So when you're doing | 0
, you're essentially doing is & 0xFFFFFFFF
. This means, any number that is represented as 0x80000000 (2147483648) or greater will return as a negative number.
For example:
// Safe
(2147483647.5918 & 0xFFFFFFFF) === 2147483647
(2147483647 & 0xFFFFFFFF) === 2147483647
(200.59082098 & 0xFFFFFFFF) === 200
(0X7FFFFFFF & 0xFFFFFFFF) === 0X7FFFFFFF
// Unsafe
(2147483648 & 0xFFFFFFFF) === -2147483648
(-2147483649 & 0xFFFFFFFF) === 2147483647
(0x80000000 & 0xFFFFFFFF) === -2147483648
(3000000000.5 & 0xFFFFFFFF) === -1294967296
Also. Bitwise operations don't "floor". They truncate, which is the same as saying, they round closest to 0
. Once you go around to negative numbers, Math.floor
rounds down while bitwise start rounding up.
As I said before, Math.floor
is safer because it operates with 64bit floating numbers. Bitwise is faster, yes, but limited to 32bit signed scope.
To summarize:
- Bitwise works the same if you work from
0 to 2147483647
.
- Bitwise is 1 number off if you work from
-2147483647 to 0
.
- Bitwise is completely different for numbers less than
-2147483648
and greater than 2147483647
.
If you really want to tweak performance and use both:
function floor(n) {
if (n >= 0 && n < 0x80000000) {
return n & 0xFFFFFFFF;
}
if (n > -0x80000000 && n < 0) {
const bitFloored = n & 0xFFFFFFFF;
if (bitFloored === n) return n;
return bitFloored - 1;
}
return Math.floor(n);
}
Just to add Math.trunc
works like bitwise operations. So you can do this:
function trunc(n) {
if (n > -0x80000000 && n < 0x80000000) {
return n & 0xFFFFFFFF;
}
return Math.trunc(n);
}
3000000000.1 | 0
evaluates to -1294967296. So this method can't be applied for money calculations (especially in cases where you multiply by 100 to avoid decimal numbers). – Td~~
for bitwise flooring.var a = ~~13.6; // a == 13
– Downcomer| 0
is simply "truncating to int" IMHO – NolittaparseInt(""+13.6)
, but it converts float to int. – Ornithic0.1 + 0.2 == 0.3
in a JavaScript console. If your language supports it, you should use a decimal type. If not, store cents instead. – CrepeNumber.MAX_SAFE_INTEGER
which is2^53 - 1
. So, the library could, for example, keep it’s internal array’s values under2^32
. In fact, that’s how JS crypto libraries work. – BocockBigInt
is in the language you should use that where possible (outside of cryptography). – Fattish