金数据技术博客 · №8

JavaScript 中与数字相关的奇怪问题

· emotionl · JavaScript

First of All

可以看下下面的几个问题然后给出自己的答案

console.log(0.1 + 0.2)
答案
0.30000000000000004
console.log(parseInt(0.0000005))
答案
5
setTimeout(() => {
  console.log("setTimeout First")
}, 2147483647)

setTimeout(() => {
  console.log("setTimeout Next")
}, 2147483648)
答案
setTimeout Next

如果你学习过前端八股文那么对 0.1 + 0.2 这个问题应该都已经有了条件反射, 至少也能说个 IEEE-754, 但是后面这两个问题如果没有遇到过就只能挠头了.

Why

我们一个问题一个问题来看

  1. 0.1 + 0.2
    这个问题应该是出现频率比较高的了, 结论就是 0.1 与 0.2 用的二进制不能完美的表达出来, 只能根据精度舍入得到近似数, 而这两个近似数得到的也是一个近似数.
    需要说明的是这个问题并不是 JavaScript 独有, Floating Point Math 展示了其他语言中 0.1 + 0.2 的结果.
    这个部分如果想深入理解可以参考 为什么 0.1 + 0.2 = 0.300000004 ?

  2. setTimeout(callback, 2147483647)
    这个问题其实能遇到的机率比较小, 因为在业务开发的时候很少会指定这么大的 timeout, 不过出于好奇可以了解这个边界在哪里.
    setTimeout 这个 API 使用一个 32 位的整数来存储其延迟值,所以任何高于这个值的东西都会引起问题
    PS: 32 位的证书中有 1 位表示符号, so 这个数为 Math.pow(2, 32-1) - 1

  3. parseInt(0.0000005)
    这个问题, 如果注意 parseInt 这个 API 参数的类型就能很好的避免了.
    parseInt 这个 API 接收的第一个参数的类型是 string. 如果传入的参数不是一个字符串,则将其转换为字符串 (使用 ToString抽象操作).
    在 number to string 的这个过程中, 如果出现以下两种情况则会用科学记数法表示

    1. 小于 1 且小数点后面带有 6 个 0 以上的浮点数值
    2. 整数位数字多于 21 位

    那么当你 parseInt(0.0000005) 其实就是在 parseInt('5e-7'), 而 parseInt('5e-7') 会忽略掉 'e-7'
    所以 parseInt(0.0000005) 的结果为 5

So

在日常的 Coding 中如果碰到和数字相关的内容需要提高警惕

And

本 Issue 会持续搜集 JavaScript 中与数字相关的奇怪问题 ~