有几种不同的方法可以在 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);
}
这样做你正在为每个 Number 对象添加一个方法。
然后你可以像这样使用它:
var x = 1.2, y = -1.2;
x.integer(); // 1
y.integer(); // -1
(-10 / 3).integer(); // -3
“最佳”方式取决于:
浮动 | 截断 | 地板 | 细胞 | 近(半高) ------+-------+-------+-------+--------------- +∞ | +∞ | +∞ | +∞ | +∞ +2.75 | +2 | +2 | +3 | +3 +2.5 | +2 | +2 | +3 | +3 +2.25 | +2 | +2 | +3 | +2 +0 | +0 | +0 | +0 | +0 钠 | 钠 | 钠 | 钠 | 钠 -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
)都表示/存储为:由于这种格式存储 1 个符号位、11 个指数位和前 53 个有效位(“尾数”),我们可以说:只有 -和之间的值可以有小数。
换句话说:所有可表示的正负值到(几乎)(此时格式称为一天)已经是整数(内部四舍五入,因为没有剩余的位来表示剩余的小数和/或最低有效位整数位)。Number
-252
+252
Number
252
2(211/2=1024)
Infinity
还有第一个“陷阱”:
您无法控制Number
内置字面量/字符串到浮点转换的 -results 的内部舍入模式(舍入模式:IEEE 754-2008“四舍五入到最接近,关系到偶数” ) 和内置算术运算(舍入模式:IEEE 754-2008 “舍入到最近”)。
例如:四舍五入并存储为:四舍五入并存储为:四舍五入并存储为:四舍五入并存储为:四舍五入并存储为:四舍五入并存储为:四舍五入并存储为:四舍五入并存储为:
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 返回您输入的整数。
为了正确地上限/下限/截断最多 x 个有效小数位的数字,我们只关心相应的最低和最高十进制小数值在舍入后是否仍会给我们一个二进制小数值(因此不会上限或下限为下一个整数)。因此,例如,如果您希望“正确”舍入(对于 ceil/floor/ trunc )最多1个
有效小数位(to比 it is to和比it is to更接近。x.1 to x.9
0.1
1/(23=8)=0.125
0
0.9
1-1/(23=8)=0.875
1
直到所有可表示的值都具有不超过第一个有效十进制小数位的非零二进制小数(值到)。
2位小数、3位小数、4位小数、5位小数、6位小数、7位小数、8位小数、9位小数、10位小数、11位小数等。±2(53-3=50)
x.1
x.9
±2(53-6=47)
±2(53-9=44)
±2(53-13=40)
±2(53-16=37)
±2(53-19=34)
±2(53-23=30)
±2(53-26=27)
±2(53-29=24)
±2(53-33=20)
±2(53-36=17)
javascript 中的“安全整数”是一个整数:
±253
±(253+1)
这有效地定义了和之间 的(安全可表示的)整数的子集范围: -253
+253
-(253 - 1) = -9007199254740991
Number.MIN_SAFE_INTEGER
to: (inclusive) (从 ES6 开始
作为静态属性提供的常量)这两个新的 ES6 常量的简单 polyfill:+(253 - 1) = +9007199254740991
Number.MAX_SAFE_INTEGER
Number.MIN_SAFE_INTEGER || (Number.MIN_SAFE_INTEGER=
-(Number.MAX_SAFE_INTEGER=9007199254740991) //Math.pow(2,53)-1
);
从 ES6 开始,还有一个免费的静态方法Number.isSafeInteger()
,用于测试传递的值是否为类型Number
并且是安全整数范围内的整数(返回布尔值true
或false
)。
注意: 也将返回false
:NaN
,Infinity
并且很明显String
(即使它代表一个数字)。
填充示例:
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()
工作方式非常简单明了:
只需截断点及其后面的数字,无论参数是正数还是负数。
我们可以进一步解释(和 polyfill)Math.trunc()
:
Math.trunc || (Math.trunc = function(n){
return n < 0 ? Math.ceil(n) : Math.floor(n);
});
请注意,与以下相比,上述 polyfill 的有效负载可能会被引擎更好地预先优化:
Math[n < 0 ? 'ceil' : 'floor'](n);
用法:Math.trunc(/* Number or String */)
输入:(整数或浮点)Number
(但会很乐意尝试将字符串转换为数字)
输出:(整数)Number
(但会很乐意尝试在字符串上下文中将数字转换为字符串)
范围:-2^52
到+2^52
(超出此范围)我们应该期望“舍入误差”(在某些时候是科学/指数表示法)简单明了,因为我们Number
在 IEEE 754 中的输入已经失去了小数精度:因为 to 之间±2^52
的数字±2^53
已经是内部舍入的整数(例如4503599627370509.5
,内部已经表示为4503599627370510
)并且超出±2^53
整数也失去精度(2的幂))。
浮点数到整数的转换通过减去一个除法的余数( %
) 1
:
示例:(result = n-n%1
或n-=n%1
)
这也应该截断浮点数。由于余数运算符的优先级高于减法,我们有效地得到:(n)-(n%1)
。
对于正数,很容易看出这限制了值:(2.5) - (0.5) = 2
,
对于负数,这限制了值:((-2.5) - (-0.5) = -2
因为--=+
so (-2.5) + (0.5) = -2
)。
由于输入和输出是与 ES6 (或它的 polyfill)相比,Number
我们应该得到相同的有用范围和输出。注意:我担心很难(不确定)可能存在差异:因为我们正在对原始数字(浮点数)和第二个派生数字(分数)进行算术运算(内部使用舍入模式“nearTiesEven”(又名银行家舍入) ) 这似乎会导致复合数字表示和算术舍入错误,因此可能最终返回一个浮点数..Math.trunc()
通过(ab-)使用按位运算将浮点数转换为整数:
这通过对 a使用按位运算在内部强制将(浮点)Number
转换(截断和溢出)转换为有符号的 32 位整数值(二进制补码) (并将结果转换回保持的(浮点)只是整数值)。 Number
Number
同样,输入和输出是Number
(以及从字符串输入到数字和数字输出到字符串的无声转换)。
更重要的强硬(通常被遗忘且未解释):
根据按位运算和数字的符号,有用范围将被限制在:
-2^31
to +2^31
(like ~~num
or num|0
or num>>0
)或 0
to +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 等等... | 等等... | 等等... | ETC... +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 等等... | 等等... | 等等... | ETC... +1.5 | +1 | +1 | +1 +0.5 | 0 | 0 | 0 0 | 0 | 0 | 0 -0.5 | 0 | 0 | 0 -1.5 | -1 | +4294967295 | -1 等等... | 等等... | 等等... | ETC... -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 等等... | 等等... | 等等... | ETC... -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 = (-2^32)-0.5 | 0 | 0 | 0 -4294967297.5 = (-2^32)-1.5 | -1 | +4294967295 | -1 -4294967298.5 = (-2^32)-2.5 | -2 | +4294967294 | -2
0
注 1:最后一列的范围已扩展到-4294967295
using (n < 0 ? -(-n>>>0) : n>>>0)
。
注意 2:按位引入了它自己的转换开销(s)(严重程度与Math
实际实现有关,因此按位可能更快(通常在较旧的历史浏览器上))。
String
开头,parseInt(/*String*/, /*Radix*/)
则将其解析为 integer 是一个合适的选择Number
。parseInt()
也会截断(对于正数和负数)。Math
方法所解释的。
最后,如果您有 aString
并期望 aString
作为输出,您还可以截断小数点和小数(与 IEEE 754 双精度浮点 ( ±2^52
) 相比,这也为您提供了更大的精确截断范围)!
例如,如果您希望从零舍入(也就是向无穷大舍入),您可以修改Math.trunc()
polyfill,例如:
Math.intToInf || (Math.intToInf = function(n){
return n < 0 ? Math.floor(n) : Math.ceil(n);
});
答案已经给出,但只是为了清楚。
为此使用数学库。圆形、天花板或地板功能。
parseInt 用于将字符串转换为 int,这不是这里需要的
toFixed 用于将浮点数转换为字符串,也不是这里需要的
由于数学函数不会对字符串进行任何转换或从字符串进行任何转换,因此它将比任何其他错误的选择都更快。
您可以使用 Number(a).toFixed(0);
甚至只是 a.toFixed(0);
编辑:
这是四舍五入到 0 位,与截断略有不同,正如其他人所建议的那样, toFixed 返回一个字符串,而不是原始整数。用于显示目的。
var num = 2.7; // typeof num is "Number"
num.toFixed(0) == "3"
var i = parseInt(n, 10);
如果您不指定基数值 like'010'
将被视为八进制(因此结果将8
不是10
)。
使用位运算符。它可能不是转换为整数的最清晰方法,但它适用于任何类型的数据类型。
假设您的函数接受一个参数value
,并且该函数以value
必须始终为整数的方式工作(并且接受 0)。然后以下任何一项将分配value
为整数:
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()
and进行比较Math.floor()
,但我喜欢额外的便利,即不会Errors
被抛出并获得0
非数字的 a
这个问题似乎是专门询问从浮点数转换为整数的问题。我的理解是这样做的方法是使用toFixed
. 所以...
var myFloat = 2.5;
var myInt = myFloat.toFixed(0);
有谁知道是否Math.floor()
比性能更高或更低Number.toFixed()
?
你也可以这样做:
var string = '1';
var integer = a * 1;
parseInt() 可能是最好的一个。a | 0
没有做你真正想要的(如果 a 是一个未定义或空值,它只是分配 0,这意味着一个空对象或数组通过了测试),并且 Math.floor 通过某种类型的诡计工作(它基本上调用 parseInt()在后台)。