因为浮点数是以二进制来存储的,而用二进制表示十进制是不能精确表示的,即使浮点数的十进制有效数字比较少,那也不一定能用二进制精确表示。为什么呢?
首先浮点数小数位的二进制是这样对应的:
小数后1位:0.5 (2^-1)
小数后2位:0.25 (2^-2)
...
小数位n位:2^-n
也就是说,任何一个浮点数的小数部分都是由2^-1 ... 2^-n组合而成的,这样就能理解为什么有效位数少的浮点数也不能精确表示了,比如0.1,就无法用上面的位数组合而精确表示出来,不信把浮点数所有位数输出来试试:
(0.1).toFixed(20)
输出:'0.10000000000000000555'
而如果把0.1换成0.5,那就可以精确表示了,因为0.5可以用2^-1精确表示啊!同理,0.625也可以。
那我们平时为什么可以直接输出0.3呢?那是因为JavaScript输出的时候默认做了舍入处理。
回到问题,把0.1*17的全部精度输出来:
(0.1*17).toFixed(20)
输出:'1.70000000000000017764'
很明显是做了舍入了。
那为什么0.1*13不会产生这样的现象呢?
(0.1*13).toFixed(20)
输出:'1.30000000000000004441'
对比一下就可以发现后面的有效数字比0.1*17少一位,舍入成0了。
数字在计算机中是十进制是以二进制存储的。
十进制中的有限不循环小数,在二进制中可能为无限循环小数
0.1(十进制) = 0.0001100110011001...(二进制)
17(十进制) = 10001(二进制)
计算机的计算结果也就是0.0001100110011001 * 10001(二进制)=1.1011001100110011(二进制) = 1.6999969482421875(十进制)
其实这是js
作浮点运算的一个bug
。
在JavsScript
中,变量在存储时并不区分number
和float
类型,而是统一按float
存储。而javascript
使用IEEE 754-2008
标准定义的64bit
浮点格式存储number
。
按照IEEE 754的定义: decimal64
对应的整形部分长度为10,小数部分长度为16,所以默认的计算结果为“1.7000000000000001”,如最后一个小数为0,则取1作为有效数字标志。
可以自己测试一下,把数字换成相对应的二进制:0.1(十进制) = 0.0001100110011001(二进制) 17(十进制) = 10001(二进制) 相乘运算。
8 回答4.8k 阅读✓ 已解决
6 回答3.5k 阅读✓ 已解决
5 回答2.9k 阅读✓ 已解决
5 回答6.4k 阅读✓ 已解决
4 回答2.3k 阅读✓ 已解决
4 回答2.8k 阅读✓ 已解决
3 回答2.5k 阅读✓ 已解决
请看sf上的这篇回答https://segmentfault.com/q/10...