parseInt(0.0000005) 等于 5 ?科学记数法也出来作怪。

1,496 阅读3分钟

神秘行为:

image.png

老规矩,还是我们先来看看前置知识

阅读本文您将收获

  • js Number 的科学计数法
  • parseInt、Number.prototype.toString方法

科学记数法

先借用一下百度百科的解释

科学记数法是一种记数的方法。把一个数表示成a与10的n次幂相乘的形式(1≤|a|<10,a不为分数形式,n为整数),这种记数法叫做科学记数法。 

例如:19971400000000=1.99714×10^13。

计算器或电脑表达10的幂是一般是用E或e,也就是1.99714E13=19971400000000。

js在何时会使用科学记数法呢?

在 ECMAScript 规范中 Number.prototype.toString 方法和 Number.prototype.toPrecision 方法中都有提到当 Number 被转化为 String 时,满足以下条件将会被处理为科学记数法(the code unit 0x0065 (LATIN SMALL LETTER E)),感兴趣可以去看一下。

  • 当一个 数字number 被转为为科学记数法后,number = a*E^n 满足 n <= -6 或者 21 < n 时,会将结果转换为科学记数法的字符串表达式

规范中还有一个用来计算 n 的比较复杂算法,原谅我太菜了没有看懂,如果有能看懂的还请在评论区指导一下 image.png

也就是说

  • 如果一个数字为小数,且整数部分为0,且小数点后6位为0,那么将被转化为科学记数法

  • 如果一个数字的整数部分 大于21 位,那么将被转化为科学记数法

我们来看几个例子

通过前面 a==1 && a==2? 看看ECMAScript规范里面自动类型转换都有啥!让人头疼的隐式转换?必然也有迹可循。 两篇文章我们已经知道 String 方法会自动为我们调用参数的 toString 方法。

console.log((1.0000001).toString()) // 1.0000001

console.log(String(1.0000001)) // 1.0000001
console.log(String(0.1111111)) // 0.1111111
console.log(String(0.0000001)) // 1e-7
console.log(String(0.00000000002)) // 2e-11

console.log(String(123456789012345678901)) // 123456789012345680000
console.log(String(1234567890123456789012)) // 1.2345678901234568e+21
console.log(String(1234567890123456789012.1)) // 1.2345678901234568e+21

parseInt 方法

262.ecma-international.org/11.0/#sec-p…

依然是看看ECMAScript 对该方法的定义,这里我只贴了与本文相关的部分,有兴趣可以去看看。

parseInt ( stringradix )

parseInt函数生成一个整数值,该整数值由根据指定 radix 解释 string 参数的内容决定。忽略 string 中的前导空格。如果 radix 未定义或为0,则假定为10,除非数字以0x或0x开头,在这种情况下,假定 radix 为16。如果 radix 为16,则数字也可以选择以0x或0x开头。

  1. Let inputString be ? ToString(string).

注意: parseInt只能将 string 的前导部分解释为整数值;它忽略任何不能被解释为整数表示法一部分的代码单元,并且没有给出任何此类代码单元被忽略的指示。

可以看出 parseInt 做的第一件事就是先将 string 参数转化为 String 类型的值,这点很重要。

我们来看看几个例子

parseInt("123") // 123
parseInt("123aaa") // 123
parseInt("1a23aaa") // 1
parseInt("a1a23aaa") // NaN


parseInt(123) // 123
parseInt(4564563) // 4564563

进入正题

有了上面 科学计数法 和 parseInt 方法的介绍。我们其实很容易就能想到 parseInt(0.0000005) === 5 的现象。我们来看一下执行过程。

parseInt(0.0000005)

首先 parseInt 方法会将参数转化为 String 类型, 我们的参数是 Number 类型, 所以将其转化为 Strng 类型会调用 Number.prototype.toString 方法。效果如下

parseInt(String(0.0000005))

上面我们已经找到, 当调用 Number.prototype.toString 方法时, 如果其值的科学计算法 a*10^n 满足 n <= -6 或者 21 < n, 就被被转化为科学记数法。

很明显 0.0000005 转化为科学计数法为 5*10^-6 , 满足转化为科学计数法表达式的条件,所以代码的效果就变成了这样。

// String(0.0000005) = "5e-6", 这个在上面介绍 Number.prototype.toString 时已经介绍过了
parseInt("5e-6")

parseInt("5e-6") 的结果为 5, 这就可以解释为什么 parseInt(0.0000005) === 5 了。

总结

  • 当调用 Number.prototype.toString 方法时, 如果其值的科学计数法 a*10^n 满足 n <= -6 或者 21 < n, 就被被转化为科学计数法。

    • 如果一个数字为小数,且整数部分为0,且小数点后6位为0,那么将被转化为科学计数法。

    • 如果一个数字的整数部分 大于21 位,那么将被转化为科学计数法。

  • parseInt 方法会先将参数转化为字符串再执行操作。