关于javascript:使用按位OR 0来设置数字

Using bitwise OR 0 to floor a number

我的一位同事偶然发现了一种方法,可以使用按位或:

1
var a = 13.6 | 0; //a == 13

我们在谈论它,想知道一些事情。

  • 它是如何工作的?我们的理论是,使用这样的运算符将数字强制转换为整数,从而删除小数部分。
  • 它与执行Math.floor相比有什么优势吗?也许快一点?(双关语不是有意的)
  • 它有什么缺点吗?也许在某些情况下不起作用?明晰是显而易见的,因为我们必须弄清楚,好吧,我在写这个问题。

谢谢。


How does it work? Our theory was that using such an operator casts the
number to an integer, thus removing the fractional part

除无符号右移(>>>外,所有位操作都处理有符号32位整数。因此,使用按位运算将浮点转换为整数。

Does it have any advantages over doing Math.floor? Maybe it's a bit
faster? (pun not intended)

http://jspef.com/or-vs-floor/2似乎稍微快一点

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.

  • 不会通过jslint。
  • 仅限32位有符号整数
  • 奇怪的比较行为:Math.floor(NaN) === NaN,而(NaN | 0) === 0


这是相对于地板的截断。霍华德的回答有点正确,但我想补充一点,Math.floor完全按照负数应该做的。从数学上讲,这就是地板。

在上面描述的情况下,程序员更感兴趣的是截断或切掉小数。不过,它们使用的语法有点掩盖了一个事实,即它们正在将浮点转换为int。


在ecmascript 6中,与|0等价的是math.trunc,我应该说:

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.

1
2
3
4
5
6
7
8
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


你的第一点是正确的。数字被转换为整数,因此任何十进制数字都将被删除。请注意,Math.floor四舍五入到下一个整数的负无穷大,因此当应用到负数时,会给出不同的结果。


  • 规范说它被转换为整数:


    Let lnum be ToInt32(lval).

  • 性能:这之前已经在JSPERF中进行过测试。

注:已删除规范的死链接


javascript将Number表示为双精度64位浮点数。

Math.floor的工作原理是这样的。

位操作在32位有符号整数中工作。32位有符号整数使用第一位作为负的能指,其他31位是数字。因此,允许的最小和最大32位有符号数字分别为-2147483648和2147483647(0x7ffffff)。

所以当你在做| 0的时候,你实际上是在做& 0xFFFFFFFF。这意味着,任何表示为0x8000000(2147483648)或更大的数字都将返回为负数。

例如:

1
2
3
4
5
6
7
8
9
10
11
 // 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

也。按位运算不"楼层"。它们截短,这与前面所说的一样,它们最接近于0。一旦转到负数,Math.floor向下取整,而按位开始取整。

正如我之前所说,Math.floor更安全,因为它使用64位浮点数。是的,按位是更快的,但仅限于32位有符号作用域。

总结:

  • 如果你是从0 to 2147483647开始工作,那么按位工作也是一样的。
  • 如果您从-2147483647 to 0工作,则位为1。
  • 对于小于-2147483648和大于2147483647的数字,按位完全不同。

如果您真的想调整性能并同时使用这两者:

1
2
3
4
5
6
7
8
9
function floor(n) {
    if (n >= 0 && n < 0x80000000) {
      return n & 0xFFFFFFFF;
    }
    if (n > -0x80000000 && n < 0) {
      return (n - 1) & 0xFFFFFFFF;
    }
    return Math.floor(n);
}

只需添加Math.trunc就可以像按位操作一样工作。所以你可以这样做:

1
2
3
4
5
6
function trunc(n) {
    if (n > -0x80000000 && n < 0x80000000) {
      return n & 0xFFFFFFFF;
    }
    return Math.trunc(n);
}