有几种不同的方法可用于在JavaScript中将浮点数转换为整数。我的问题是哪种方法能够提供最佳性能、最兼容或被认为是最佳实践?以下是我所知道的一些方法:
var a = 2.5;
window.parseInt(a); // 2
Math.floor(a); // 2
a | 0; // 2
我相信还有其他选择。有什么建议吗?
有几种不同的方法可用于在JavaScript中将浮点数转换为整数。我的问题是哪种方法能够提供最佳性能、最兼容或被认为是最佳实践?以下是我所知道的一些方法:
var a = 2.5;
window.parseInt(a); // 2
Math.floor(a); // 2
a | 0; // 2
我相信还有其他选择。有什么建议吗?
parseInt偶尔被用来将浮点数转换为整数。但是,它非常不适合这个任务,因为如果它的参数是数字类型,则首先会将其转换为字符串,然后解析为数字...
对于将数字舍入到整数的情况,Math.round、Math.ceil和Math.floor更加适合使用...
显然,双重按位非是将数字向下取整的最快方法:
var x = 2.5;
console.log(~~x); // 2
以前这里有一篇文章,现在却出现了404错误:http://james.padolsey.com/javascript/double-bitwise-not/
谷歌已经缓存了它:http://74.125.155.132/search?q=cache:wpZnhsbJGt0J:james.padolsey.com/javascript/double-bitwise-not/+double+bitwise+not&cd=1&hl=en&ct=clnk&gl=us
但是Wayback Machine拯救了一天!http://web.archive.org/web/20100422040551/http://james.padolsey.com/javascript/double-bitwise-not/
以下内容来自Douglas Crockford的《Javascript: The Good Parts》:
Number.prototype.integer = function () {
return Math[this < 0 ? 'ceil' : 'floor'](this);
}
这样做会向每个数字对象添加一个方法。
然后您可以像这样使用它:
var x = 1.2, y = -1.2;
x.integer(); // 1
y.integer(); // -1
(-10 / 3).integer(); // -3
'最佳'方式取决于:
float | trunc | floor | ceil | near (half up) ------+-------+-------+-------+--------------- +∞ | +∞ | +∞ | +∞ | +∞ +2.75 | +2 | +2 | +3 | +3 +2.5 | +2 | +2 | +3 | +3 +2.25 | +2 | +2 | +3 | +2 +0 | +0 | +0 | +0 | +0 NaN | NaN | NaN | NaN | NaN -0 | -0 | -0 | -0 | -0 -2.25 | -2 | -3 | -2 | -2 -2.5 | -2 | -3 | -2 | -2 -2.75 | -2 | -3 | -2 | -3 -∞ | -∞ | -∞ | -∞ | -∞对于浮点数到整数的转换,我们通常期望“截断”(又称“向零舍入”,又称“远离无穷大舍入”)。
String
Number
String
(默认基数为10)(在屏幕上)Number
类型的所有数字在javascript中都以"IEEE 754 Double Precision Floating Point (binary64)"格式表示/存储。因此,整数也以相同的浮点格式表示(作为没有分数的数字)。注意:大多数实现在可能的情况下内部使用更有效(用于速度和内存大小)的整数类型!
由于这种格式存储1个符号位、11个指数位和前53位有效数字(“尾数”),因此我们可以说:只有介于-2的52次方和+2的52次方之间的数字值才能有小数部分。换句话说:所有可表示的正负数字值在2的52次方到(几乎)2的(2的11次方除以2=1024)次方之间都是整数(内部四舍五入,因为没有剩余的比特来表示剩余的小数和/或最不重要的整数位数)。
这里有一个需要注意的地方:
你无法控制 Number
类型在内部转换为浮点数时的舍入模式(舍入模式:IEEE 754-2008 "round to nearest, ties to even"),以及内置的算术操作的舍入模式(舍入模式: IEEE 754-2008 "round-to-nearest")。
例如:
252+0.25 = 4503599627370496.25
被舍入并存储为:4503599627370496
252+0.50 = 4503599627370496.50
被舍入并存储为:4503599627370496
252+0.75 = 4503599627370496.75
被舍入并存储为:4503599627370497
252+1.25 = 4503599627370497.25
被舍入并存储为:4503599627370497
252+1.50 = 4503599627370497.50
被舍入并存储为:4503599627370498
252+1.75 = 4503599627370497.75
被舍入并存储为:4503599627370498
252+2.50 = 4503599627370498.50
被舍入并存储为:4503599627370498
252+3.50 = 4503599627370499.50
被舍入并存储为:4503599627370500
Number
需要一个小数部分(并且至少有一个位来表示它),否则ceil/floor/trunc/near将返回您输入的整数。0.1
比0
更接近1 /(2 3=8)= 0.125
,而0.9
比1-1 /(2 3=8)= 0. 875
更接近1
。±2(53-3=50)
以内的所有可表示值才会在小数点后第一位(值为x.1
到x.9
)之后拥有非零二进制分数。对于2个小数位,使用±2(53-6=47)
,对于3个小数位,使用±2(53-9=44)
,对于4个小数位,使用±2(53-13=40)
,对于5个小数位,使用±2(53-16=37)
,对于6个小数位,使用±2(53-19=34)
,对于7个小数位,使用±2(53-23=30)
,对于8个小数位,使用±2(53-26=27)
,对于9个小数位,使用±2(53-29=24)
,对于10个小数位,使用±2(53-33=20)
,对于11个小数位,使用±2(53-36=17)
等等。±253
(作为完全的2的幂)可以被准确地表示,它也不是一个安全整数,因为在它被舍入以适应最高的53位时,它也可能变成±(253+1)
)。这有效地定义了一个范围子集,在 -253和+253之间(安全可表示的整数):
-(253 - 1) = -9007199254740991
(inclusive)Number.MIN_SAFE_INTEGER
since ES6)to: +(253 - 1) = +9007199254740991
(inclusive)
(a constant provided as static property Number.MAX_SAFE_INTEGER
since ES6)
Trivial polyfill for these 2 new ES6 constants:
Number.MIN_SAFE_INTEGER || (Number.MIN_SAFE_INTEGER=
-(Number.MAX_SAFE_INTEGER=9007199254740991) //Math.pow(2,53)-1
);
Number.isSafeInteger()
,用于测试传递的值是否为Number
类型,并且是在安全整数范围内的整数(返回布尔值true
或false
)。NaN
、Infinity
和显然的String
(即使它表示一个数字),也会返回false
。Number.isSafeInteger || (Number.isSafeInteger = function(value){
return typeof value === 'number' &&
value === Math.floor(value) &&
value < 9007199254740992 &&
value > -9007199254740992;
});
ECMAScript 2015 / ES6提供了一个新的静态方法Math.trunc()
用于将浮点数截断为整数:
返回数字x的整数部分,去除任何小数位。如果x已经是整数,则结果为x。
或者更简单地说(MDN):
与其他三个Math方法不同:
Math.floor()
、Math.ceil()
和Math.round()
,Math.trunc()
的工作方式非常简单明了:
只需截断小数点及其后面的数字,无论参数是正数还是负数。
我们可以进一步解释(并填充)Math.trunc()
如下:
Math.trunc || (Math.trunc = function(n){
return n < 0 ? Math.ceil(n) : Math.floor(n);
});
Math[n < 0 ? 'ceil' : 'floor'](n);
用法: Math.trunc(/* 数字或字符串 */)
数字
(但是会尝试将字符串转换为数字)数字
(但在字符串上下文中,也会尝试将数字转换为字符串)-2^52
到 +2^52
(超出此范围我们应该期望出现“舍入误差”(在某些时候科学/指数符号表示),因为我们的 IEEE 754 中的 Number
输入已经失去了小数精度: 因为介于 ±2^52
和 ±2^53
之间的数字已经被“内部四舍五入”为整数 (例如,4503599627370509.5
内部已经表示为 4503599627370510
),而超过 ±2^53
的整数也会失去精度(2 的幂))。通过减去除以1
的余数Remainder (%
)来将浮点数转换为整数:
例如:result = n-n%1
(或者n-=n%1
)
这也应该截断浮点数。由于余数运算符比减法运算符的优先级更高,因此我们实际上得到:(n)-(n%1)
。
对于正数,很容易看出这会向下取整:(2.5) - (0.5) = 2
,
对于负数,这会向上取整:(-2.5) - (-0.5) = -2
(因为--=+
,所以(-2.5) + (0.5) = -2
)。
既然输入和输出都是Number
类型,与 ES6 的 Math.trunc()
(或它的 polyfill)相比,我们应该获得相同的有用范围和输出。
注意:虽然我担心(不确定)可能存在差异:因为我们正在对原始数字(浮点数)和第二个派生数字(分数)进行算术运算(在内部使用舍入模式“ nearTiesEven”(又名 Banker's Rounding)),这似乎会引发复合数字表示和算术舍入误差,因此可能仍然返回一个浮点数。
利用位运算将浮点数转换为整数:
这是通过在Number
上使用位运算(截断和溢出)将其强制转换为带符号的32位整数值(二进制补码),内部实现的(结果会被转换回只包含整数值的(浮点)Number
)。Number
(并且再次从字符串输入自动转换为数字,并从数字输出自动转换为字符串)。-2^31
到+2^31
(例如~~num
或num|0
或num>>0
)或0
到+2^32
(num>>>0
)。 n | n>>0或n<<0或 | n>>>0 | n < 0 ? -(-n>>>0) : n>>>0
| n|0或n^0或~~n | |
| 或n&0xffffffff | |
----------------------------+-------------------+-------------+---------------------------
+4294967298.5 = (+2^32)+2.5 | +2 | +2 | +2
+4294967297.5 = (+2^32)+1.5 | +1 | +1 | +1
+4294967296.5 = (+2^32)+0.5 | 0 | 0 | 0
+4294967296 = (+2^32) | 0 | 0 | 0
+4294967295.5 = (+2^32)-0.5 | -1 | +4294967295 | +4294967295
+4294967294.5 = (+2^32)-1.5 | -2 | +4294967294 | +4294967294
等等... | 等等... | 等等... | 等等...
+2147483649.5 = (+2^31)+1.5 | -2147483647 | +2147483649 | +2147483649
+2147483648.5 = (+2^31)+0.5 | -2147483648 | +2147483648 | +2147483648
+2147483648 = (+2^31) | -2147483648 | +2147483648 | +2147483648
+2147483647.5 = (+2^31)-0.5 | +2147483647 | +2147483647 | +2147483647
+2147483646.5 = (+2^31)-1.5 | +2147483646 | +2147483646 | +2147483646
等等... | 等等... | 等等... | 等等...
+1.5 | +1 | +1 | +1
+0.5 | 0 | 0 | 0
0 | 0 | 0 | 0
-0.5 | 0 | 0 | 0
-1.5 | -1 | +4294967295 | -1
等等... | 等等... | 等等... | 等等...
-2147483646.5 = (-2^31)+1.5 | -2147483646 | +2147483650 | -2147483646
-2147483647.5 = (-2^31)+0.5 | -2147483647 | +2147483649 | -2147483647
-2147483648 = (-2^31) | -2147483648 | +2147483648 | -2147483648
-2147483648.5 = (-2^31)-0.5 | -2147483648 | +2147483648 | -2147483648
-2147483649.5 = (-2^31)-1.5 | +2147483647 | +2147483647 | -2147483649
-2147483650.5 = (-2^31)-2.5 | +2147483646 | +2147483646 | -2147483650
等等... | 等等... | 等等... | 等等...
-4294967294.5 = (-2^32)+1.5 | +2 | +2 | -4294967294
-4294967295.5 = (-2^32)+0.5 | +1 | +1 | -4294967295
-4294967296 = (-2^32) | 0 | 0 | 0
-4294967296.5 = (-
注意1:最后一列使用(n < 0 ? -(-n>>>0) : n>>>0)扩展范围到0至-4294967295。
注意2:位运算引入了自己的转换开销(s),其严重性与Math方法取决于实际实现,因此在旧的历史浏览器上,位运算可能会更快一些。
显然,如果您的“浮点数”一开始就是一个字符串,parseInt(/*String*/, /*Radix*/)将是将其解析为整数Number的适当选择。
parseInt()也会截断正数和负数。
范围再次限制为IEEE 754双精度浮点数,如上所述的Math方法。
最后,如果您有一个String
并期望输出一个String
,您也可以截取小数点和小数部分(与IEEE 754双精度浮点数(±2^52
)相比,这还给您提供了更大的准确截断范围)!
额外信息:
从上面的信息中,您现在应该知道所有需要知道的内容。
例如,如果您想要远离零四舍五入(又名向正无穷方向舍入),您可以修改Math.trunc()
polyfill,例如:
Math.intToInf || (Math.intToInf = function(n){
return n < 0 ? Math.floor(n) : Math.ceil(n);
});
答案已经给出,但为了更清楚明白,请使用Math库。round、ceil或floor函数。
parseInt用于将字符串转换为整数,这不是需要的。
toFixed用于将浮点数转换为字符串,也不是需要的。
由于Math函数不会执行任何字符串的转换,因此它比其他选择更快,而且其他选择都是错误的。
var num = 2.7; // typeof num is "Number"
num.toFixed(0) == "3"
var i = parseInt(n, 10);
'010'
将被视为八进制(因此结果将是8
而不是10
)。200000000000000000000parseInt(2e20)
2 - Andrew B.parseInt(2e21)
value = ~~(value)
value = value | 0;
value = value & 0xFF; // one byte; use this if you want to limit the integer to
// a predefined number of bits/bytes
最好的部分是,它适用于字符串(例如从文本输入中获取的内容),这些字符串是数字 ~~("123.45") === 123
。任何非数字值都会导致结果为0
,即,
~~(undefined) === 0
~~(NaN) === 0
~~("ABC") === 0
0x
前缀)。~~("0xAF") === 175
我想这里涉及到一些类型转换。我会进行一些性能测试,将它们与 parseInt()
和 Math.floor()
进行比较,但我喜欢额外的便利性,即不会抛出错误,并在非数字情况下得到一个 0
。
这个问题似乎是在特别询问如何将浮点数转换为整数。我的理解是,可以使用 toFixed
来实现这一点。因此...
var myFloat = 2.5;
var myInt = myFloat.toFixed(0);
有人知道Math.floor()
与Number.toFixed()
哪个更快吗?
.toFixed
的结果始终为 String
,而 Math.floor
的结果始终为 Number
。 - Kokizzu