0.1+0.2结果为 0.30000000000000004的背后

前端开发同学或多或少都应该看到过0.1 + 0.2 === 0.30000000000000004这个经典问题。 我当初也是抱着好奇的态度看了相关文章, 但是过眼云烟,只是了解了下会有这种特别的情况就浅尝辄止了。最近在js中用到浮点数计算踩坑了,才静下心来去仔细了解下原理。
在js中计算浮点数时,因为存储大小的限制,导致精度丢失,所以在浮点数的运算中,都要注意。

浮点数的格式存储
在JS中,无论整数还是小数都是Number类型,它的实现遵循IEEE 754,是标准的Double双精度浮点数, 使用固定的64位来表示。
实际上, JS中的数字都会转化为二进制存储下来, 由于数字存储限定了64位,但现实世界中,数字是无穷的, 所以一定会有数字超出这个存储范围。超出这个范围的数字在存储时就会丢失精度。

正整数转成二进制:除二取余,然后倒序排列,高位补零。
将正的十进制数除以二,得到的商再除以二,依次类推直到商为零或一时为止,然后在旁边标出各步的余数,最后倒着写出来,高位补零就行了。

同时,我们都知道, 整数十进制转二进制时,是除以二去余数,这是可以除尽的。但我们可能不知道的是,小数十进制转化为二进制的计算方法是,小数部分*2,取整数部分,直至小数部分为0,如果永远不为零,在超过精度时的最后一位时0舍入1。

/* 0.1 转化为二进制的计算过程 */

0.1 * 2 = 0.2 > 取0
0.2 * 2 = 0.4 > 取0
0.4 * 2 = 0.8 > 取0
0.8 * 2 = 1.6 > 取1
0.6 * 2 = 1.2 > 取1
0.2 * 2 = 0.4 > 取0
...
后面就是循环了

到这里, 我们就可以发现一些端倪了

// 使用toString(2), 将10进制输出为二进制的字符串
0.1.toString(2);
// "0.00011001100110011001100110011001100110011001100110011001100..."

0.2.toString(2);
// "0.001100110011001100110011001100110011001100110011001100110011..."

// 二进制相加结果, 由于超过精度, 取52位, 第53位舍0进1

"0.010011001100110011001100110011001100110011001100110011,1"
// 最后存储下来的结果是
const s = "0.010011001100110011001100110011001100110011001100110100"
// 用算法处理一下。
a = 0;
s.split('').forEach((i, index) => { a += (+i/Math.pow(2, index+1))});
// a >> 0.30000000000000004

个人理解:0.1在js中会转换为二进制来进行存储,由于超出了存储范围,在存储时就会丢失精度。
那么如何计算0.1+0.2呢?
toFixed() 方法可把 Number 四舍五入为指定小数位数的数字。
方法一:

(0.1* 10+0.2*10)/10 ===0.3 //返回true

方法二:

parseFloat((0.1+0.2).toFixed(10)) ===0.3 //返回true

(toFixed() 方法可把 Number 四舍五入为指定小数位数的数字;
parseFloat() 函数可解析一个字符串,并返回一个浮点数。)


image.png

此外在比较0.1+0.2==0.3的时候结果为false,
正确的比较方式:

Math.abs(0.1+0.2-0.3)<=Number.EPSILON
//检查等式左右两边差的绝对值,是否小于最小精度。

参考文章:
https://www.cnblogs.com/login123/p/12100916.html
https://segmentfault.com/a/1190000005022170

你可能感兴趣的:(0.1+0.2结果为 0.30000000000000004的背后)