admin管理员组文章数量:1133768
A colleague of mine stumbled upon a method to floor float numbers using a bitwise or:
var a = 13.6 | 0; //a == 13
We were talking about it and wondering a few things.
- How does it work? Our theory was that using such an operator casts the number to an integer, thus removing the fractional part
- Does it have any advantages over doing
Math.floor
? Maybe it's a bit faster? (pun not intended) - Does it have any disadvantages? Maybe it doesn't work in some cases? Clarity is an obvious one, since we had to figure it out, and well, I'm writing this question.
Thanks.
A colleague of mine stumbled upon a method to floor float numbers using a bitwise or:
var a = 13.6 | 0; //a == 13
We were talking about it and wondering a few things.
- How does it work? Our theory was that using such an operator casts the number to an integer, thus removing the fractional part
- Does it have any advantages over doing
Math.floor
? Maybe it's a bit faster? (pun not intended) - Does it have any disadvantages? Maybe it doesn't work in some cases? Clarity is an obvious one, since we had to figure it out, and well, I'm writing this question.
Thanks.
Share Improve this question edited Sep 2, 2023 at 10:07 david.pfx 10.9k3 gold badges33 silver badges65 bronze badges asked Sep 20, 2011 at 15:47 Alex TurpinAlex Turpin 47.7k23 gold badges116 silver badges146 bronze badges 13 | Show 8 more comments7 Answers
Reset to default 199How does it work? Our theory was that using such an operator casts the number to an integer, thus removing the fractional part
All bitwise operations except unsigned right shift, >>>
, work on signed 32-bit integers. So using bitwise operations will convert a float to an integer.
Does it have any advantages over doing Math.floor? Maybe it's a bit faster? (pun not intended)
http://jsperf.com/or-vs-floor/2 seems slightly faster
Does it have any disadvantages? Maybe it doesn't work in some cases? Clarity is an obvious one, since we had to figure it out, and well, I'm writting this question.
- Will not pass jsLint.
- 32-bit signed integers only
- Odd Comparative behavior:
Math.floor(NaN) === NaN
, while(NaN | 0) === 0
This is truncation as opposed to flooring. Howard's answer is sort of correct; But I would add that Math.floor
does exactly what it is supposed to with respect to negative numbers. Mathematically, that is what a floor is.
In the case you described above, the programmer was more interested in truncation or chopping the decimal completely off. Although, the syntax they used sort of obscures the fact that they are converting the float to an int.
In ECMAScript 6, the equivalent of |0
is Math.trunc, kind of I should say:
Returns the integral part of a number by removing any fractional digits. It just truncate the dot and the digits behind it, no matter whether the argument is a positive number or a negative number.
Math.trunc(13.37) // 13
Math.trunc(42.84) // 42
Math.trunc(0.123) // 0
Math.trunc(-0.123) // -0
Math.trunc("-1.123")// -1
Math.trunc(NaN) // NaN
Math.trunc("foo") // NaN
Math.trunc() // NaN
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 than2147483647
.
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);
}
Your first point is correct. The number is cast to an integer and thus any decimal digits are removed. Please note, that Math.floor
rounds to the next integer towards minus infinity and thus gives a different result when applied to negative numbers.
The specs say that it is converted to an integer:
Let lnum be ToInt32(lval).
Performance: this has been tested at jsperf before.
note: dead link to spec removed
var myNegInt = -1 * Math.pow(2, 32);
var myFloat = 0.010203040506070809;
var my64BitFloat = myNegInt - myFloat;
var trunc1 = my64BitFloat | 0;
var trunc2 = ~~my64BitFloat;
var trunc3 = my64BitFloat ^ 0;
var trunc4 = my64BitFloat - my64BitFloat % 1;
var trunc5 = parseInt(my64BitFloat);
var trunc6 = Math.floor(my64BitFloat);
console.info(my64BitFloat);
console.info(trunc1);
console.info(trunc2);
console.info(trunc3);
console.info(trunc4);
console.info(trunc5);
console.info(trunc6);
IMO: The question "How does it work?", "Does it have any advantages over doing Math.floor?", "Does it have any disadvantages?" pale in comparison to "Is it at all logical to use it for this purpose?"
I think, before you try to get clever with your code, you may want to run these. My advice; just move along, there is nothing to see here. Using bitwise to save a few operations and having that matter to you at all, usually means your code architecture needs work. As far as why it may work sometimes, well a stopped clock is accurate twice a day, that does not make it useful. These operators have their uses, but not in this context.
本文标签: javascriptUsing bitwise OR 0 to floor a numberStack Overflow
版权声明:本文标题:javascript - Using bitwise OR 0 to floor a number - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736765365a1951786.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
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). – Šime Vidas Commented Sep 20, 2011 at 16:080.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. – Alex Turpin Commented Sep 28, 2019 at 2:11