我的一个同事偶然发现了一个方法,使用位浮点数或:
var a = 13.6 | 0; //a == 13
我们一直在讨论这件事,想知道一些事情。
它是如何工作的?我们的理论是使用这样的运算符将数字转换为整数,从而去掉小数部分 它比math。floor有什么优势吗?也许会快一点?(并非有意双关语) 它有什么缺点吗?也许在某些情况下它不起作用?清晰是显而易见的,因为我们必须弄清楚,好吧,我在写这个问题。
谢谢。
我的一个同事偶然发现了一个方法,使用位浮点数或:
var a = 13.6 | 0; //a == 13
我们一直在讨论这件事,想知道一些事情。
它是如何工作的?我们的理论是使用这样的运算符将数字转换为整数,从而去掉小数部分 它比math。floor有什么优势吗?也许会快一点?(并非有意双关语) 它有什么缺点吗?也许在某些情况下它不起作用?清晰是显而易见的,因为我们必须弄清楚,好吧,我在写这个问题。
谢谢。
它是如何工作的?我们的理论是使用这样的运算符转换 将数字改为整数,从而去掉小数部分
除无符号右移>>>外,所有的位操作都适用于有符号的32位整数。因此,使用位操作将浮点数转换为整数。
它比math。floor有什么优势吗?也许有点 更快呢?(并非有意双关语)
http://jsperf.com/or-vs-floor/2似乎快一点
它有什么缺点吗?也许在某些情况下它不起作用? 清晰度是显而易见的,因为我们必须弄清楚, 我在写这个问题。
不会传递jsLint。 仅32位有符号整数 奇数比较行为:Math.floor(NaN) === NaN,而(NaN | 0) === 0
这是截断,而不是叠加。霍华德的回答是正确的;但我要补充的是数学。对于负数,它做的就是它应该做的。从数学上讲,这就是地板。
在上面描述的情况中,程序员更感兴趣的是截断或将小数点完全砍掉。尽管,他们使用的语法掩盖了他们将浮点数转换为int型的事实。
在ECMAScript 6中,相当于|0的是Math。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
Javascript将数字表示为双精度64位浮点数。
数学。地板的工作就考虑到了这一点。
位操作适用于32位有符号整数。32位有符号整数使用第一位作为负符号,其余31位为数字。因此,允许的32位有符号数的最小和最大数目分别为- 2147,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的地方。一旦你碰到负数,数学。楼层向下四舍五入,同时开始按位向上四舍五入。
就像我之前说的,数学。Floor更安全,因为它使用64位浮点数操作。按位更快,是的,但仅限于32位有符号的范围。
总结:
如果从0工作到2147483647,按位工作也是一样的。 如果从-2147483647到0,则位差1。 对于小于-2147483648和大于2147483647的数字,按位是完全不同的。
如果你真的想调整性能并同时使用:
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);
}
再加上数学。Trunc的工作方式类似于位操作。所以你可以这样做:
function trunc(n) {
if (n > -0x80000000 && n < 0x80000000) {
return n & 0xFFFFFFFF;
}
return Math.trunc(n);
}
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:“它是如何工作的?”,“它比Math.floor有什么优点吗?”,“它有什么缺点吗?”与“为此目的使用它是否合乎逻辑?”相比显得苍白无力。
我认为,在你试图巧妙地处理你的代码之前,你可能想要运行这些。我的建议;往前走吧,这里没什么可看的。使用位来节省一些操作,这对你来说很重要,通常意味着你的代码架构需要改进。至于为什么它有时会工作,一个停止的时钟一天准确两次,这并没有使它有用。这些运算符有它们的用途,但不是在这个上下文中。