彻底弄懂:0.1+0.2 != 0.3

4,142 阅读3分钟

前言

  可能很多人看过这道面试题,老生常谈了,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个月前了。写博客还是挺累的,别看这篇文章短短的,也要各种翻资料,各种找图。因为和自己记笔记不太一样,自己记笔记可能记个大概就好,但是写博客总是要尽可能的讲清楚问题,每个问题都要弄清楚,还是挺麻烦的。但是好处也是多多,正如最好的学习方法就是广为人知的费曼技巧,尽量用自己的话把复杂的东西说的简单传授给别人,在这个过程中,你可能会发现一些之前没有发现的问题,并且从别人身上得到一些反馈,去验证自己的想法是否正确。加油共勉!!