详解js中0.1+0.2!=0.3


笔者曾经面试被问到过 0.1+0.2 的结果是啥,初看这题你可能心中会想,难道不是0.3吗?但你肯定会觉得没那么简单,那今天我们就来探一探究竟。

1. JavaScript中数字的存储机制

在JavaScript中数字是以 IEEE 754 双精度64位浮点数(需VPN)来存储的,它的表示格式为:

(s) * (m) * (2 ^ e)
其中s表示符号位,m表示尾数,占52位,e表示指数,占11位,根据ECMAScript 5 规范,e 的范围是 **[-1074, 971]**,这样可以得出js能表示的最大值为1 * (2^53 - 1) * (2^971) = 1.7976931348623157e+308,而这个值恰好是**Number.MAX_VALUE**的值;同理可以推出js能表示的大于0的最小值是1 * 1 * (2 ^ -1074) = 5e-324,这个值恰好是**Number.MIN_VALUE**的值。

这里你可能注意到了,m是52位,能表示的最大值是2 ^ 52 - 1,可这里为什么是53呢?这就涉及到了隐藏位,比如1 * 1.00111100 * 2 ^ -3中,m表示的是1.00111100中的小数部分“00111100”,整数部分1就是隐藏位,这也就是说,只要指数不全为0,那么它的隐藏位就是1。这里顺便提一下,js里整数可以被精确表示的范围是-2 ^ 53 + 1 ~ 2 ^ 53 - 1。

2. 数字表示

现在我们回到这道题目来,我们知道在计算机中,数字都是以二进制存储的,所以我们要先将0.1和0.2转化成二进制,对于十进制转二进制,整数部分除二取余,倒序排列,小数部分乘二取整,顺序排列,所以

0.1 转化为二进制

0.0 0011 0011 0011 0011 0011 0011 … (0011循环

0.2 转化为二进制

0.0011 0011 0011 0011 0011 0011 0011 … (0011循环

然后我们用之前说过的 IEEE 754 双精度64位浮点数(需VPN)来表示:

// 0.1
e = -4;
m = 1.1001100110011001100110011001100110011001100110011010 (52)

// 0.2
e = -3;
m = 1.1001100110011001100110011001100110011001100110011010 (52)

当然,这里的m指的是小数点后的52位,而小数点前的整数部分1就是前面说过的隐藏位。

然后我们把它相加,这里有一个问题,就是指数不一致时,应该怎么处理,一般是往右移,因为即使右边溢出了,损失的精度远远小于左移时的溢出。

e = -4; m = 1.1001100110011001100110011001100110011001100110011010 (52位)

+

e = -3; m = 1.1001100110011001100110011001100110011001100110011010 (52位)


e = -3; m = 0.1100110011001100110011001100110011001100110011001101 (52位)

+

e = -3; m = 1.1001100110011001100110011001100110011001100110011010 (52位)


e = -3; m = 10.0110011001100110011001100110011001100110011001100111 (52位)


e = -2; m = 1.00110011001100110011001100110011001100110011001100111 (53位)

我们看到已经溢出来了(超过了52位),那么这个时候我们就要做四舍五入了,那怎么舍入才能与原来的数最接近呢?比如1.101要保留2位小数,那么结果有可能是1.10和1.11,这个时候两个都是一样近,我们取哪一个呢?规则是保留偶数的那一个,在这里就是保留1.10。

回到我们之前的就是取m=1.0011001100110011001100110011001100110011001100110100 (52位)

然后我们得到最终的二进制数

1.0011001100110011001100110011001100110011001100110100 * 2 ^ -2

=0.010011001100110011001100110011001100110011001100110100

现在转化为十进制,二进制小数转化为十进制的方法是小数点后第一位*2 ^  -1第二位*2 ^ -2,以此类推,最终我们用等比数列的求和公式得到十进制数为0.30000000000000004,所以0.1 + 0.2的最终结果是

0.30000000000000004

这就是整个推理过程,希望你也能试着推理一下,虽说这属于JavaScript里面比较偏的知识,但对于了解计算机的存储机制还是有好处的。

参考

https://www.cnblogs.com/shytong/p/5091600.html


文章作者: Twittytop
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Twittytop !
评论
  目录