前言
可能很多人看过这道面试题,老生常谈了,0.1+0.2 === 0.3 结果是false,网上很多解释说是因为二进制的问题导致误差,这个问题我觉得最恶心的是
为什么0.2+0.2是正常的,0.1+0.1也是正常的,0.1+0.2就有问题了呢?它们就没有二进制转换的问题了吗?到底是为什么?
正文
计算机是如何存储数字的?
首先我们需要了解一下,计算机存储数字都是以多位二进制的方式进行存储的。而js没有分浮点数和整数,全部统一为number类型,都利用IEEE754标准的64位双精度浮点数存储。
IEEE754标准64位双精度浮点数
图片来自来 维基百科
这里看起来可能不太好理解,直接来举个例子吧。
假设一个数字10000,这是一个10进制的,转换为2进制应该是多少?
就是10011100010000了
然后这里会涉及到一个二进制的科学计数法。
首先我们来看一下我们比较熟悉的10进制的科学计数法。
那么2进制的科学计数法呢?也是差不多的。就是底数变了。
所以上面的10000应该长
所以最后的结果和这个公式看起来就十分相似了。这里再来看一个图。
所以根据IFEEE754的64位标准来转换,就是这样把数据存储起来的。
好了。那么看来,真正决定精度问题的,就在于fraction的那52位2进制的数字了。
所以这就说明了为什么在js中的有效整数数字2^(-53)~2^53(不包括),因为52位的二进制,并不代表js只能存储那么点大的数字,超出范围也是可以的。只是精度可能会出现问题
小数问题
根据上文我们知道,如果我们存储的数字转为2进制后的有效数字大于52位,就会出现精度丢失的问题。那么回到最开始的题目,0.1+0.2 != 0.3是为什么呢?
我们来看看0.1和0.2转化为2进制会怎么样
转化之后的结果会是各种无线不循环小数,看回之前的公式
超出52位的会被处理掉,所以当涉及js中的浮点数计算时,会产生各种奇怪的问题因为你完全不知道,什么时候误差会被省略了,什么时候误差刚好对上是正确的结果。
解决方案
那么到最后,既然发现了问题,肯定要有解决方案。
在es6中,Number有个新的属性EPSILON,在计算机科学技术里面,这个单词的意思为极小值。
就是意味着,如果误差小于这个数,就没有意义了,可以看作是没有误差。
当然,也可以使用toFixed去解决问题
因为一般前端对于数字的准确性并不会要求太高,往往是保留一定数位小数即可。
结尾
距离上次写掘金文章已经是4个月前了。写博客还是挺累的,别看这篇文章短短的,也要各种翻资料,各种找图。因为和自己记笔记不太一样,自己记笔记可能记个大概就好,但是写博客总是要尽可能的讲清楚问题,每个问题都要弄清楚,还是挺麻烦的。但是好处也是多多,正如最好的学习方法就是广为人知的费曼技巧,尽量用自己的话把复杂的东西说的简单传授给别人,在这个过程中,你可能会发现一些之前没有发现的问题,并且从别人身上得到一些反馈,去验证自己的想法是否正确。加油共勉!!