金数据技术博客 · №8
JavaScript 中与数字相关的奇怪问题
First of All
可以看下下面的几个问题然后给出自己的答案
console.log(0.1 + 0.2)答案
0.30000000000000004
console.log(parseInt(0.0000005))
答案
5setTimeout(() => {
console.log("setTimeout First")
}, 2147483647)
setTimeout(() => {
console.log("setTimeout Next")
}, 2147483648)答案
setTimeout Next
如果你学习过前端八股文那么对 0.1 + 0.2 这个问题应该都已经有了条件反射, 至少也能说个 IEEE-754, 但是后面这两个问题如果没有遇到过就只能挠头了.
Why
我们一个问题一个问题来看
-
0.1 + 0.2
这个问题应该是出现频率比较高的了, 结论就是 0.1 与 0.2 用的二进制不能完美的表达出来, 只能根据精度舍入得到近似数, 而这两个近似数得到的也是一个近似数.
需要说明的是这个问题并不是 JavaScript 独有, Floating Point Math 展示了其他语言中 0.1 + 0.2 的结果.
这个部分如果想深入理解可以参考 为什么 0.1 + 0.2 = 0.300000004 ? -
setTimeout(callback, 2147483647)
这个问题其实能遇到的机率比较小, 因为在业务开发的时候很少会指定这么大的 timeout, 不过出于好奇可以了解这个边界在哪里.
setTimeout 这个 API 使用一个 32 位的整数来存储其延迟值,所以任何高于这个值的东西都会引起问题
PS: 32 位的证书中有 1 位表示符号, so 这个数为Math.pow(2, 32-1) - 1 -
parseInt(0.0000005)
这个问题, 如果注意 parseInt 这个 API 参数的类型就能很好的避免了.
parseInt 这个 API 接收的第一个参数的类型是 string. 如果传入的参数不是一个字符串,则将其转换为字符串 (使用 ToString抽象操作).
在 number to string 的这个过程中, 如果出现以下两种情况则会用科学记数法表示- 小于 1 且小数点后面带有 6 个 0 以上的浮点数值
- 整数位数字多于 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 中与数字相关的奇怪问题 ~