在JavaScript中将浮点数转换为整数的最佳方法是什么?

时间:2008-09-25 03:53:49

标签: javascript variables types

在JavaScript中有几种不同的方法可以将浮点数转换为整数。我的问题是,哪种方法可以提供最佳性能,最兼容,或者被认为是最佳实践?

以下是我所知道的一些方法:

var a = 2.5;
window.parseInt(a); // 2
Math.floor(a);      // 2
a | 0;              // 2

我确信那里还有其他人。建议?

12 个答案:

答案 0 :(得分:38)

根据this website

  

parseInt偶尔用作将浮点数转换为整数的方法。它非常适合该任务,因为如果它的参数是数字类型,它将首先转换为字符串,然后解析为数字...

     

对于将数字四舍五入为整数,Math.round,Math.ceil和Math.floor之一最好...

答案 1 :(得分:8)

来自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 :(得分:7)

显然双按位 - 不是最快的方法来放置数字:

var x = 2.5;
console.log(~~x); // 2

以前是一篇文章,现在获得404:http://james.padolsey.com/javascript/double-bitwise-not/

Google已将其缓存: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/

答案 3 :(得分:4)

答案已经给出,但只是要明确。

使用数学库进行此操作。圆形,细胞或地板功能。

parseInt用于将字符串转换为int,这不是此处所需的

toFixed用于将float转换为字符串,而不是这里需要的

由于Math函数不会对字符串进行任何转换,因此它会比任何其他错误的选择更快。

答案 4 :(得分:3)

您可以使用数字(a).toFixed(0);

甚至只是a.toFixed(0);

修改

这是四舍五入到0位,与截断略有不同,并且正如其他人所建议的那样,toFixed返回一个字符串,而不是一个原始整数。用于显示目的。

var num = 2.7;  // typeof num is "Number"
num.toFixed(0) == "3"

答案 5 :(得分:3)

最好的&#39;方式取决于

  • 舍入模式:您期望/需要的type of rounding(浮点数到整数) 对于具有小数部分的正数和/或负数 常见例子:
    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
    -∞    |   -∞  |   -∞  |   -∞  |   -∞  
    
    对于浮动到整数转换,我们通常期望&#34;截断&#34;
    (又名&#34;向零走&#34; 又名&#34;远离无穷大&#34; )。
    实际上这只是'切断'&#39;浮点数的小数部分 大多数技术和(内部)内置方法都是这样的。
  • 输入:如何表示您的(浮点)数字:
    • String
      通常基数/基数:10(十进制)
    • 浮点(&#39;内部&#39;)Number
  • 输出:您想要对结果值执行的操作:
    • (中间)输出String(默认基数10)(在屏幕上)
    • 对结果值进行进一步计算
  • 范围:
    在什么数值范围内,您期望输入/计算结果
    你希望哪个范围相应的正确&#39;输出。

只有这些考虑因素得到解答后,我们才能考虑适当的方法和速度!

<小时/> 根据ECMAScript 262规范:javascript中的所有数字(类型Number)表示/存储在:
&#34; IEEE 754 Double Precision Floating Point (binary64)&#34;格式。
所以整数相同的浮点格式表示(作为没有分数的数字)。
注意:大多数实现在可能的情况下使用更高效(对于速度和内存大小)整数类型内部

由于此格式存储1个符号位,11个指数位和前53个有效位(&#34;尾数&#34;),我们可以说: only Number - -252+252之间的值可以有一小部分 换句话说:所有可表示正面和负面Number - 值介于 252到(几乎)2(211/2=1024)之间(此时格式称为一天 Infinity)已经是整数(内部舍入,因为没有剩下的位来表示剩余的小数和/或最低有效整数位数。)

还有第一个&#39; gotcha&#39;: 您无法控制Number的内部舍入模式 - 内置Literal / String的结果为浮动转换(舍入模式:IEEE 754-2008&#34;舍入到最近,与连接到#34) ;)和内置算术运算(舍入模式:IEEE 754-2008&#34; round-to-nearest&#34;)。
例如:
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会返回你输入的整数。

要正确ceil / floor / trunc一个数字到x重要的小数十进制数字,我们只关心相应的最低和最高小数小数值是否仍然会在舍入后给我们一个二进制小数值(所以不被ceiled或者下一个整数) 因此,例如,如果您希望&#39;纠正&#39;舍入(对于ceil / floor / trunc)最多1个重要的小数十进制数(x.1 to x.9),我们需要至少 3位(而非4)才能给我们 a 二进制小数值:
0.11/(23=8)=0.125更接近00.9更接近1-1/(23=8)=0.875而不是1

最多±2(53-3=50)所有可表示的值都具有非零二进制分数,且不超过第一个有效小数位数(值{{ 1}}到x.1)。
2个小数x.9,3个小数±2(53-6=47),4个小数±2(53-9=44),5个小数±2(53-13=40),6个小数±2(53-16=37),7个小数{{ 1}},8位小数±2(53-19=34),9位小数±2(53-23=30),10位小数±2(53-26=27),11位小数±2(53-29=24)等。

javascript中的&#34;安全整数&#34; 是一个整数:

  • 可以完全表示为IEEE-754双精度数字,
  • 其IEEE-754表示不能是舍入任何其他整数以符合IEEE-754表示的结果
    (即使±2(53-33=20)(精确的2的幂)可以完全表示,但是一个安全整数,因为它之前也可能是±2(53-36=17)四舍五入以适应最多53个最重要的位。)

这有效地定义了 ±253±(253+1)之间(可安全表示的)整数的子集范围:

  • 来自:-253(含)
    (自ES6以来作为静态属性Number.MIN_SAFE_INTEGER提供的常量)
  • to:+253(含)
    (自ES6以来作为静态属性Number.MAX_SAFE_INTEGER提供的常量)
    这两个新ES6常数的普通填料:

    -(253 - 1) = -9007199254740991


从ES6开始,还有一个免费的静态方法Number.isSafeInteger(),它测试传递的值是否为+(253 - 1) = +9007199254740991类型,并且是安全整数范围内的整数(返回布尔值Number.MIN_SAFE_INTEGER || (Number.MIN_SAFE_INTEGER= -(Number.MAX_SAFE_INTEGER=9007199254740991) //Math.pow(2,53)-1 ); 或{{1 }})。
注意:还会返回Numbertruefalse,显然false(即使它代表一个数字)。
Polyfill 示例

NaN

<强> ECMAScript 2015 / ES6 provides a new static method Math.trunc()
将浮点数截断为整数:

  

返回数字x的整数部分,删除任何小数位数。如果x已经是整数,则结果为x。

或者更简单(MDN):

  

与其他三种数学方法不同:InfinityStringNumber.isSafeInteger || (Number.isSafeInteger = function(value){ return typeof value === 'number' && value === Math.floor(value) && value < 9007199254740992 && value > -9007199254740992; }); Math.floor()的工作方式非常简单明了:
  只要截断点和它后面的数字,无论参数是正数还是负数。

我们可以进一步解释(和polyfill)Math.ceil()

Math.round()

注意,与以下相比,上述polyfill的有效负载可能可能更好地预先优化: 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);(但很乐意尝试在字符串上下文中将Number转换为String)
范围Math.trunc(/* Number or String */)Number(超出此范围我们应该期待&#39;舍入错误&#39;(以及在某些时候科学/指数表示法)简单而且仅仅因为我们在IEEE 754中的Number输入已经失去了小数精度:因为-2^52+2^52之间的数字已经是内部舍入的整数(例如Number是内部已经表示为±2^52)并且超出±2^53整数也松散精度(2的幂))。

<小时/>

通过4503599627370509.5减去分割的Remainder4503599627370510)来浮动到整数转换:

示例:±2^53(或%
这也应该截断浮点数。由于剩余运算符的值比减法高precedence,因此我们实际得到:1 对于正数,很容易看出这个价值为result = n-n%1
对于负数,这会使值为n-=n%1(因为(n)-(n%1)所以(2.5) - (0.5) = 2)。

由于输入输出(-2.5) - (-0.5) = -2,我们获取相同的有用范围和输出与ES6 --=+(或它的polyfill)相比 注意:强硬我恐惧(不确定)可能存在差异:因为我们正在做算术(内部使用舍入模式&#34; nearTiesEven&#34;(又名Banker&#39; s舍入))在原始数字(浮点数)和第二个派生数字(分数)这似乎邀请复合数字表示和算术舍入错误,因此可能会返回一个浮点数。

<小时/>

使用bitwise operations通过(ab-)浮动到整数转换:

这可以通过内部强制执行(浮点)(-2.5) + (0.5) = -2转换(截断和溢出)到带符号的32位整数值(两个补码)通过在Number上使用按位运算(并将结果转换回(浮点)Math.trunc(),它只保存整数值)。

同样,输入输出Number(再次从String-input到Number和Number-output到String的静默转换)。

更重要的是强硬(通常被遗忘而未解释):
取决于按位操作和数字符号有用的范围 限制 之间:
NumberNumber(例如Number-2^31+2^31 ~~numnum|0num>>0)。

这应该通过以下查找表进一步说明(包含所有&#39;关键&#39;示例):

              n             | n>>0 OR n<<0 OR   |    n>>>0    | n < 0 ? -(-n>>>0) : n>>>0
                            | n|0 OR n^0 OR ~~n |             |
                            | OR 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...               |         etc...    |      etc... |      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...               |         etc...    |      etc... |      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...               |         etc...    |      etc... |      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...               |         etc...    |      etc... |      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

注意1:最后一列使用0将范围+2^32扩展到num>>>0
注2:按位引入自己的转换开销( s )(严重性vs 0取决于实际实现,因此按位可以更快(通常在较旧的历史记录上)浏览器))。

<小时/> 显然,如果你的浮点数是多少?数字是一个-4294967295开始,
(n < 0 ? -(-n>>>0) : n>>>0)是将其解析为整数Math的合适选择 String也会截断(正数和负数) 范围再次限制为IEEE 754双精度浮点,如上面针对parseInt(/*String*/, /*Radix*/)方法所解释的那样。

最后,如果您有一个Number并期望parseInt()作为输出,您还可以切断小数点和分数(与IEEE 754双精度相比,这也会提供更大的精确截断范围浮点(Math))!

<小时/> EXTRA:
从上面的信息,你现在应该有你需要知道的一切。

例如,如果你想要从零开始(又名向无穷大舍入),你可以修改String polyfill,用于示例

String

答案 6 :(得分:2)

var i = parseInt(n, 10);

如果您未指定像'010'这样的基数值将被视为八进制(因此结果将为8而不是10)。

答案 7 :(得分:1)

使用按位运算符。它可能不是转换为整数的最清晰的方法,但它适用于任何类型的数据类型。

假设您的函数采用参数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()Math.floor()进行比较,但我希望有一些额外的便利,即没有Errors被抛出并获得0非编号

答案 8 :(得分:1)

所以我在Chrome上做了一个基准,当输入已经是一个数字时,最快的是~~numnum|0,半速:Math.floor,以及最慢的是parseInthere

benchmark result

编辑:似乎已经有另一个人制作了rounding基准(更多结果)和其他waysnum>>0(与{{一样快) 1}})和|0(有时快)

答案 9 :(得分:0)

问题似乎是专门询问从float转换为int。我的理解是,这样做的方法是使用toFixed。所以......

var myFloat = 2.5;
var myInt = myFloat.toFixed(0);

是否有人知道Math.floor()的效果是否比Number.toFixed()更高或更低?

答案 10 :(得分:-1)

你也可以这样做:

var string = '1';
var integer = a * 1;

答案 11 :(得分:-4)

parseInt()可能是最好的。 a | 0没有做你真正想要的事情(如果a是未定义或空值,它只指定0,这意味着空对象或数组通过测试),Math.floor通过某种类型的技巧工作(它基本上在后台调用parseInt()。