为什么JavaScript中0.1+0.2不等于0.3,如何解决JavaScript中精度缺失的问题
在大多数静态类型语言中都采用多种数据类型来保存数组,比如在C语言中我们有Int
, Long
, Float
, Double
这四种数据类型来分别保存4字节的整形,8字节的整形,4字节的浮点型和8字节的浮点型数字。而在Javascript中则只有一种number数据类型来保存数字,实际上Javascript中的number数据类型是基本符合 IEEE 754-2008 规定的双精度浮点数,类似于C语言的Double数据类型。而0.1+0.2不等于0.3的问题实际上在大部分使用浮点数的语言中都存在。
要弄明白文章开头的Javascript精度缺失问题,我们首先就要从问题的源头-浮点数在计算机中的表示方法开始了解:
IEEE 754 双精度表示
根据国际标准IEEE 754,任意一个二进制浮点数V可以表示成下面的形式:
注:S代表符号位,当S=0时, V>0, 当S=1时, V<0;M代表有效数字,大于等于0, 小于2;E为指数位
所有数字都可以表示为科学计数法,比如十进制的5表示为科学计数法就是,用二进制表示就是:
那么代入公式可得 。
IEEE754标准规定64位的双精度浮点数在计算机中存储时,最高的1位为符号位S,接着11位表示指数E,剩余52位表示有效数字M。(对于32位单精度浮点数符号位占用一位,指数E占用8位,有效数字M占用23位)
到这里,你可能会觉得5是这样存储的:
但事实上,根据IEEE754规定,对于有效数字M和指数E的存储有一点特殊规定:
所有数字都是科学计数法转化过来的,对于二进制的规范科学计数法,所有数字都可以表示为的形式,既然整数位始终都是1我们也就没有必要浪费一位来存储,只需要执行计算的时候硬件补回来即可,所以对于我们实际上只保存。
对于指数E,在64位的双精度浮点数表示中,我们的指数位占用了11位来存储,可以表示的数字范围就是,但在科学计数法中指数是可能为负数的。所以为了表示负数,IEEE754标准规定,E表示的指数是使用真实值减去一个中间值得到的。对于双精度浮点数,这个中间值是1023,所以要表示的指数,。
最后,对于指数E还有几种特殊情况:
(1)当指数位全部为时,有效数字不再加上整数位的,这种情况表示,以及接近于的很小的数字。
(2)当指数位全为时,如果有效数字M全为0,则表示±Infinity,如果M不全为0,这表示NaN。
终上所述,我们数字5在计算机中的双精度浮点数表示就是:
0.1 + 0.2 不等于0.3
现在再来看一开始说的。
首先将表示为科学计数法:
再将两个数转化为指数部分相同,再相加:
最后的结果有效数字部分比52位多了一位,四舍五入为, 所以最后的表示为:
之所以最后计算的结果与我们的认知出现了偏差就是因为在计算的过程中,0.1和0.2在计算机中存储的就是一个四舍五入后的值,计算后的结果也经过了四舍五入,最终导致了与我们认知上的偏差。