使用按位或 0 对数字求底

IT技术 javascript floating-point bit-manipulation
2021-01-18 23:29:39

我的一位同事偶然发现了一种使用按位或来计算浮点数的方法:

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

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

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

谢谢。

6个回答

它是如何工作的?我们的理论是使用这样的运算符将数字转换为整数,从而去除小数部分

除了无符号右移之外的所有按位运算都>>>适用于有符号的 32 位整数。因此,使用按位运算会将浮点数转换为整数。

它比做 Math.floor 有什么优势吗?也许它会快一点?(双关语不是故意的)

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

它有什么缺点吗?也许在某些情况下它不起作用?清晰度是显而易见的,因为我们必须弄清楚,好吧,我正在写这个问题。

  • 不会通过 jsLint。
  • 仅限 32 位有符号整数
  • 奇怪的比较行为:Math.floor(NaN) === NaN, 而(NaN | 0) === 0
(value | 0) === value可用于检查一个值是否实际上是一个整数并且只是一个整数(如链接的 Elm 源代码@dwayne-crooks)。并且foo = foo | 0可用于将任何值强制为整数(其中 32 位数字被截断,所有非数字变为 0)。
2021-03-20 23:29:39
@harold 确实,因为它实际上并不圆,只是截断了。
2021-03-22 23:29:39
另一个可能的缺点是Math.floor(NaN) === NaN,虽然(NaN | 0) === 0. 这种差异在某些应用程序中可能很重要。
2021-03-24 23:29:39
这是asm.js(我第一次了解到它的地方)的标准部分如果没有其他原因,它会更快,因为它没有调用Math对象上的函数,该函数可以随时替换为Math.floor = function(...).
2021-04-06 23:29:39
由于循环不变代码运动,您的 jsperf 正在为 chrome 上的空循环产生性能信息。一个稍微好一点的性能测试是:jsperf.com/floor-performance/2
2021-04-08 23:29:39

这是截断而不是地板。霍华德的回答有点正确;但我想补充一点,Math.floor它完全符合负数的预期。从数学上讲,这就是地板。

在上面描述的情况下,程序员更感兴趣的是截断或完全切掉小数点。虽然,他们使用的语法有点掩盖了他们将浮点数转换为 int 的事实。

你是对的乍得。当我测试Math.floor(-5.5) 它会返回给我-6因此,如果我们使用按位,它将按位使用-5.5 >> 0它会返回正确答案-5
2021-03-25 23:29:39
这是正确的答案,接受一个不是。添加到它会Math.floor(8589934591.1)产生预期的结果,8589934591.1 | 0 DOES NOT
2021-04-04 23:29:39

在ECMAScript中6,相当于|0Math.trunc,善良的我应该说:

通过删除任何小数位返回数字的整数部分。它只是截断点及其后面的数字,无论参数是正数还是负数。

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.trunc()使用大于或等于 2^31 的数字并且| 0
2021-04-07 23:29:39

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

Javascript 表示Number双精度 64 位浮点数

Math.floor 考虑到这一点。

按位运算适用于 32 位有符号整数。32 位有符号整数使用第一位作为负号,其他 31 位是数字。因此,允许的 32 位有符号数的最小值和最大值分别为 -2,147,483,648 和 2147483647 (0x7FFFFFFFF)。

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

例如:

 // 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.
  • 对于小于-2147483648和大于 的数字,按位完全不同2147483647

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

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像按位操作一样的工作。所以你可以这样做:

function trunc(n) {
    if (n > -0x80000000 && n < 0x80000000) {
      return n & 0xFFFFFFFF;
    }
    return Math.trunc(n);
}