ParseInt()的迷幻行为

最近看到一片文章 Solving a Mystery Behavior of parseInt() in JavaScript,讲的是JavaScript中的parseInt()的一个奇怪现象。

parseInt()是JavaScript内置的一个方法,能将数字型的字符串转换成整数。例如, 将 '100'转换成100

const number = parseInt('100'); console.log(number); // 100

parseInt(numbericalString, radix)也可以接受第二个参数,解析一个字符串并返回指定基数的十进制整数, radix 是2-36之间的整数,表示被解析字符串的基数,常用的是2,8,10和16。例如

const number = parseInt('100', 2); console.log(number); // 4 const number = parseInt('100', 8); console.log(number); // 64

以上是parseInt()的基本用法。

parseInt()的一个奇怪行为

praseInt(numbericalString) 会将其第一个参数转换为一个字符串,对该字符串进行解析,然后返回一个整数或 NaN,所以我们可以用 parseInt() 方法来获取浮点数的整数。

parseInt(0.5); // => 0 parseInt(0.05); // => 0 parseInt(0.005); // => 0 parseInt(0.0005); // => 0 parseInt(0.00005); // => 0 parseInt(0.000005); // => 0

获取0.5,0.05这样的浮点数的整数返回0,这看起来没有任何问题。但是 parseInt() 会讲0.0000005 转换成5。

parseInt(0.0000005); // 5

这到底是为什么呢?

解开谜团

前面提到 parseInt 执行时,会先将第一个参数转换成字符串(ECMASCript ParseInt())。其具体步骤如下,

  1. 声明inputString = String(string)。
  2. 声明 S, 保存去掉 inputString 开头的空格之后的字符串,如果 inputString 是空字符串则返回空字符串。
  3. 声明标记位 sign = 1。
  4. 如果S不为空且第一个字符是减号-sign设置为-1
  5. 如果S不为空且第一个字符是加号+或者减号-,移除第一个字符串。
  6. 声明R=ToInt32(radix)
  7. 声明stripPrefix = true
  8. 如果 R !== 0,则
  • a. 如果R < 2 或者 R > 36,返回NaN。
  • b. 如果 R !== 16,设置stripPrefix = false
  1. 如果 R == 0
  • a. 设置 R = 10
  1. 如果 stripPrefix === true
  • a. 如果S的长度大于2且前两个字符是0x或者0X,移除前两个字符,同时设置R = 16
  1. 如果S包含了当前R进制不相关的字符(假定第一个这样的字符是Q),设置Z为S的一个子字符串,这个子字符串是Q之前的所有字符。否则设置Z=S
  2. 如果 Z 为空,返回 NaN
  3. 声明 mathInt 为Z在当前R进制下的整数
  4. 声明 numbermathInt
  5. 返回 sign x number

那我们先用 String() 来测试

String(0.5); // => '0.5' String(0.05); // => '0.05' String(0.005); // => '0.05' String(0.0005); // => '0.005' String(0.00005); // => '0.00005' String(0.000005); // => '0.000005' String(0.0000005); // => '5e-7'

可以看到String(0.0000005)返回了科学计数法的格式5e-7,那么按照上面提到的 parseInt() 的实际处理过程,确实是返回5。由于某些数字在其字符串表示形式中使用e字符(例如 6.022×23 表示 6.022e23 ),因此当对非常大或非常小的数字使用数字时,使用 parseInt 截断数字将产生意外结果。

将浮点数转换成整数的另一个更加安全的是 Math.floor()

Math.floor(0.5); // => 0 Math.floor(0.05); // => 0 Math.floor(0.005); // => 0 Math.floor(0.0005); // => 0 Math.floor(0.00005); // => 0 Math.floor(0.000005); // => 0 Math.floor(0.0000005); // => 0

那 parseInt(999999999999999999999) 返回多少呢?

参考文章

  1. Solving a Mystery Behavior of parseInt() in JavaScript
  2. MDN ParseInt
  3. ECMASCript ParseInt()