在JavaScript中如何将浮点数转换为整数?

1520
我想在JavaScript中将一个浮点数转换为整数。实际上,我想知道如何进行标准的两种转换:截断和四舍五入。而且要高效,不通过转换为字符串并解析来完成。

102
如果您不知道,JavaScript 中的所有数字都是浮点数。根据规范: - some
8
4.3.20 数字类型:数字类型是一组表示数字的值。在ECMAScript中,这组值代表双精度64位格式IEEE 754值,包括特殊的“非数字”(NaN)值、正无穷大和负无穷大。 - some
13
是的,JavaScript没有明确的“整数”类型,但需要进行这种转换仍然很常见。例如,在我的应用程序中,用户输入了一个数字(可能包括美分)。我需要截断美分并使用逗号显示。第一步是转换为整数。 - mcherm
1
另外还有一个有用的链接:所有方法速度比较 http://jsperf.com/math-floor-vs-math-round-vs-parseint/33 - c..
@mcherm 为什么不捕获小数点并禁止其使用呢?这样做不是更好的用户体验吗?此外,如果您的代码只允许用户输入[0..9],那么您就不必转换为整数。这仍然是一个有用的问题。 - Karl
1
@karl: 如果我正在接受字段输入,我可能能够控制我所接受的字符,但是我可能会在Javascript中进行各种处理,而不仅仅是接受用户输入。即使这样,我也可能希望支持粘贴等功能。 - mcherm
18个回答

2179
var intvalue = Math.floor( floatvalue );
var intvalue = Math.ceil( floatvalue ); 
var intvalue = Math.round( floatvalue );

// `Math.trunc` was added in ECMAScript 6
var intvalue = Math.trunc( floatvalue );

Math 对象参考文档


示例

正数
// value=x        //  x=5          5<x<5.5      5.5<=x<6  

Math.floor(value) //  5            5            5
Math.ceil(value)  //  5            6            6
Math.round(value) //  5            5            6
Math.trunc(value) //  5            5            5
parseInt(value)   //  5            5            5
~~value           //  5            5            5
value | 0         //  5            5            5
value >> 0        //  5            5            5
value >>> 0       //  5            5            5
value - value % 1 //  5            5            5
负面
// value=x        // x=-5         -5>x>=-5.5   -5.5>x>-6

Math.floor(value) // -5           -6           -6
Math.ceil(value)  // -5           -5           -5
Math.round(value) // -5           -5           -6
Math.trunc(value) // -5           -5           -5
parseInt(value)   // -5           -5           -5
value | 0         // -5           -5           -5
~~value           // -5           -5           -5
value >> 0        // -5           -5           -5
value >>> 0       // 4294967291   4294967291   4294967291
value - value % 1 // -5           -5           -5
正数 - 较大的数字
// x = Number.MAX_SAFE_INTEGER/10 // =900719925474099.1

// value=x            x=900719925474099    x=900719925474099.4  x=900719925474099.5
           
Math.floor(value) //  900719925474099      900719925474099      900719925474099
Math.ceil(value)  //  900719925474099      900719925474100      900719925474100
Math.round(value) //  900719925474099      900719925474099      900719925474100
Math.trunc(value) //  900719925474099      900719925474099      900719925474099
parseInt(value)   //  900719925474099      900719925474099      900719925474099
value | 0         //  858993459            858993459            858993459
~~value           //  858993459            858993459            858993459
value >> 0        //  858993459            858993459            858993459
value >>> 0       //  858993459            858993459            858993459
value - value % 1 //  900719925474099      900719925474099      900719925474099
负数 - 更大的数字
// x = Number.MAX_SAFE_INTEGER/10 * -1 // -900719925474099.1

// value = x      // x=-900719925474099   x=-900719925474099.5 x=-900719925474099.6

Math.floor(value) // -900719925474099     -900719925474100     -900719925474100
Math.ceil(value)  // -900719925474099     -900719925474099     -900719925474099
Math.round(value) // -900719925474099     -900719925474099     -900719925474100
Math.trunc(value) // -900719925474099     -900719925474099     -900719925474099
parseInt(value)   // -900719925474099     -900719925474099     -900719925474099
value | 0         // -858993459           -858993459           -858993459
~~value           // -858993459           -858993459           -858993459
value >> 0        // -858993459           -858993459           -858993459
value >>> 0       //  3435973837           3435973837           3435973837
value - value % 1 // -900719925474099     -900719925474099     -900719925474099

90
如另一个回答所提到的,可以使用var intValue = ~~floatValue;来进行负数安全截断。如果这种表示方法过于晦涩难懂,请将其隐藏在函数中:function toInt(value) { return ~~value; }。(如果需要,这也可以将字符串转换为整数。) - Keen
6
如果这个回答附带输入/输出的示例,我会点赞。 - J. Random Coder
11
关于评论中提到的限制值为32位有符号整数,而Math.floor/ceil/round可以处理高达53位(Number.MAX_SAFE_INTEGER 9007199254740991)。此在下面的回答中已经提到,但值得在这里重申,以便那些阅读这些评论的人了解。 - John
3
在下方多个位置阅读:Math.trunc(val); 之所以这是被接受的答案,请注意。 - Old Badman Grey
1
不适用于像 2.3 - 2.3%1 这样的精确值。 - Lapys

342

按位或运算符

按位或运算符可用于截断浮点数,对于正数和负数都有效:

function float2int (value) {
    return value | 0;
}

结果

float2int(3.1) == 3
float2int(-3.1) == -3
float2int(3.9) == 3
float2int(-3.9) == -3

性能比较?

我创建了一个JSPerf测试,比较了以下代码的性能:

  • Math.floor(val)
  • val | 0 按位
  • ~~val 按位
  • parseInt(val)

这些都仅适用于正数。在这种情况下,你可以放心地使用按位运算,以及Math.floor函数。

但是,如果您的代码需要同时处理正数和负数,那么按位操作是最快的方法(按位是首选)。另一个JSPerf测试对这些进行了比较,在这个测试中很明显,由于额外的符号检查,Math现在是四个中最慢的

注意

如评论所述,按位运算符对有符号32位整数进行操作,因此大数字将被转换,例如:

1234567890  | 0 => 1234567890
12345678901 | 0 => -539222987

11
除了无符号右移操作,所有按位操作都作用于有符号32位整数。因此,在浮点数上使用按位操作会将它们转换为整数,并去除小数点后的数字。 - Robert Koritnik
按位取反在速度方面非常强大。 - Murtaza Khursheed Hussain
4
如果你只需要针对正数,使用Math.floor()更快(至少根据我在 Google Chrome 30.0.1599.101 版本上运行你的第一个 JSPerf 测试 的结果),更健壮(因为它不依赖于数字以二进制形式表示的方式,而这种位运算解决方案可能会受到影响并被破坏),最重要的是,更明确。 - ma11hew28
9
请注意,位运算符操作的是32位数字。对于太大无法放入32位的数字,这些运算符将无法使用。 - Kat
2
~~ is better because it's a unary operator. 4.2|0+4 equals 4 but ~~4.2+4 equals 8 - Janus Troelsen
显示剩余6条评论

101
注意:您不能使用Math.floor()来替换截断,因为Math.floor(-3.1) = -4而不是-3
截断的正确替代方法是:
function truncate(value)
{
    if (value < 0) {
        return Math.ceil(value);
    }

    return Math.floor(value);
}

1
这取决于负数期望的行为。有些使用需要将负数映射到更负的值(-3.5 -> -4),而有些需要将它们映射到较小的整数(-3.5 -> -3)。前者通常称为“floor”。单词“truncate”经常用于描述任一行为。在我的情况下,我只会输入负数。但是对于那些关心负数行为的人来说,这个注释是一个有用的警告。 - mcherm
29
他们似乎没有正确理解“truncate”的含义。如其名,截断就是截掉数字。一般情况下,它不等同于floor或ceil。http://en.wikipedia.org/wiki/Truncation - Thanatos
6
Math.trunc(value) 是在 ECMAScript 6 中添加的。 - 4esn0k
4
floor 向负无穷取整, truncate 向零取整 (ceil 向正无穷取整)。 - Peter Cordes

50

1
这可能不是生产代码的好做法(因为它很晦涩),但这正是我在JS中进行代码高尔夫比赛时所需要的我的<canvas>字体渲染引擎。谢谢! - Kragen Javier Sitaker
11
这也可以通过 n | 0 实现。 - Jay Douglass
20
请注意,使用这两种方法(~~n或n|0)只适用于小于等于2^31-1或2147483647的数字。如果输入的数值大于2147483647,则会返回错误的结果。例如,2147483647 | 0 返回-2147483648,4294967295 | 0 返回-1,这几乎肯定不是您想要的结果。 - Ed Bayiates

43

对于截断:

var intvalue = Math.floor(value);

关于round函数:

var intvalue = Math.round(value);

7
Math.floor不会截断负数值。请参见上面的答案。除此之外,回答很好。 - oligofren
如果您对性能感兴趣,我在这里放了一个小的测试案例:http://jsperf.com/dsafdgdfsaf/2 (var | 0 在这里胜出)。 - Cybolic

41
您可以使用parseInt方法进行无舍入取整。由于0x(十六进制)和0(八进制)前缀选项,请小心处理用户输入。
var intValue = parseInt(floatValue, 10);

编辑:根据评论部分的警告,请注意某些数字值将转换为其指数形式,例如1e21,这将导致"1"的不正确十进制表示。


2
即使只是简单截断,这似乎也是最慢的方法。http://jsperf.com/float-to-int-conversion-comparison - Robert Koritnik
2
始终将第二个值传递给parseInt以指定您期望的基数。因此,使用parseInt(floatValue, 10)始终获得基数10。 - Tim Tisdall
4
虽然这已经是一个老问题了,但似乎它经常被问到,所以我会把这个警告放在这里。如果一个数因为太大而使用“e”表示法,结果将只显示一位数字,而不是预期的结果。例如,parseInt(1000000000000000000000, 10); 的结果是1,而不是1 000 000 000 000 000 000 000。无论如何,问题明确不想要“将其转换为字符串并解析”,尽管这相对较小... ;) - Qantas 94 Heavy
6
@Qantas94Heavy这种行为的原因是,因为parseInt()期望它的第一个参数为字符串而不是数字。当您传递此整数时,它会被转换为1e21,然后parseInt解析字符串1e21,其结果为1 - Olaf Dietsche
1
在上面的评论中添加另一个例子,parseInt(0.0000005) 的结果是 5 而不是 0,因为它首先被转换为 5e-7。因此,在解析整数而不是字符串时要小心一些是很重要的。 - Rohan Chougule
显示剩余2条评论

19

将二进制数左移0位等同于除以1

// >> or >>>
2.0 >> 0; // 2
2.0 >>> 0; // 2

4
小提示:>> 0 只适用于整数小于 _2^31-1_,而 >>> 0 只适用于整数小于 _2^32-1_。对于更大的值,这将返回0。 - Romuald Brunet
@RomualdBrunet,是的,JavaScript明确定义所有位运算均基于32位数字进行。这在规范中有说明。 - Alexis Wilke
这类似于Javascript只使用32位(有符号)整数进行按位操作,就像上面的答案所述。因此,任何似乎无效的位操作(例如移位0、与0或1、双重非运算)仍然需要Javascript解释器将值转换为32位整数。 - FrankKrumnow

14

在您的情况下,当您想要在最后插入逗号的字符串时,您也可以使用Number.toFixed()函数,但是这将进行四舍五入操作。


提醒一下,toFixed的返回值是一个字符串。例如:$num1 = 12.2222.toFixed(2) $num2 = Number.parseFloat(12.2222).toFixed(2),$num1和$num2都是字符串"12.22"。 - tinystone

14
如果你想要一个完整的、向下取整的答案:
var intvalue = Math.floor( floatvalue );
var integer = Math.floor(4.56);
Answer = 4

如果您想向上取整:

var intvalue = Math.ceil( floatvalue );
Answer would be = 5

13

还有一种可能的方法 — 使用异或运算:

console.log(12.3 ^ 0); // 12
console.log("12.3" ^ 0); // 12
console.log(1.2 + 1.3 ^ 0); // 2
console.log(1.2 + 1.3 * 2 ^ 0); // 3
console.log(-1.2 ^ 0); // -1
console.log(-1.2 + 1 ^ 0); // 0
console.log(-1.2 - 1.3 ^ 0); // -2

按位运算的优先级低于数学运算,这很有用。 在https://jsfiddle.net/au51uj3r/上试试。


不错,但是要小心。如果你有 const num = -165.01295398373 * 10_000_000_000_000,那么 Math.trunc(num) 将会给你正确的 -1 650 129 539 837 300,但是 num ^ 0 却会给出 1 190 253 196。(注意,Number.MIN_SAFE_INTEGER = -9 007 199 254 740 991) - 8bitjoey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接