Skip to content

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

Description

@emotionl

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 中如果碰到和数字相关的内容需要提高警惕

  • 小数
    🌰:0.1、0.2
  • 特别大的数
    🌰:2147483648(2 的 32 次方减 1)
  • 数字的加减乘除
    🌰:0.1 + 0.2、1111.11 + 1111.11 + 1111.11 + 1111.11 + 1111.11
  • 调用某些 API 的时候需要注意参数的类型 , 避免隐式类型的转换
    🌰:parseInt(0.0000005)

And

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions