0.1 + 0.2 !== 0.3 四舍五入引发的惨案 记录下我的一步步探索的过程


问题描述

js中,类似0.1 + 0.2 === 0.3、0.01 + 0.2 === 0.3会得到结果为false, 0.2 + 0.2 === 0.4却为true,以下针对该问题进行一些分析。
在这里插入图片描述


一、js如何判断相等?

首先看下判断0.1 + 0.2时为什么不等于0.3。js 的‘=== ’ 运算比较数值类型的
时候是通过直接判断两个数的存储时的二进制表示是否相同以确定是否相等的。

0.1的二进制是0.0001100110011001100…(1100循环),0.2的二进制是:0.00110011001100…(1100循环)

但是由于js的浮点数采用IEEE 754标准,小数部分最小只能精确到2的-52次方,二进制表示的小数点后52位,十进制的小数点后第16位(Number.EPSILON),小于该部分的数值就会由于四舍五入导致产生一些误差,这也是为什么0.1 + 0.2 结果为什么不等于0.3(四舍五入一个舍了一位一个进了一位,两数不一样了),所以在部分情况下产生了这种偏差导致计算结果与预期的并不一样。
浏览器控制台里也可以试下: 0.1转为二进制表示const a = (0.1 + 0.2).toString(2)
为’0.0100110011001100110011001100110011001100110011001101’, 而0.3的二进制表示const b = 0.3.toString(2) 为 ’0.010011001100110011001100110011001100110011001100110011‘; 判断a === b得到了false的结果,于是我们得出了0.1 + 0.2 确实是不等于0.3的。
不相等的情况

二、探索js判断两数相等的最小误差值

在这里插入图片描述
经过以上探索,但是发现并不对,.01 + 0.2 - .21 < Math.pow(2, -54) // true; 说明最小的值并不是Math.pow(2, -54), 而是Math.pow(2, -55) ,js中使用toString(2)所能表示的二进制数也仅能能表示到第55位,所以第56位就会 1进位 0舍去, 等式两边值相差小于Math.pow(2,-55)的,都会是成立的。
在这里插入图片描述
通过以上过程的探究,最终我们可以确定,js内运算时允许的最小误差不应超过Math.pow(2, -55), 小于该误差的运算均会被认为是成立的。
补充:
在这里插入图片描述
.1 + Math.pow(2, -57) - .1 < Math.pow(2 , -55) 这一步并不严谨,如果是按57位相加减,差值为Math.pow(2, -57),
也会得到 true 的结果, 于是此处追加一些论证。 .2 + Math.pow(2, -56) === .2 // true, 进一步证明运算过程中小于Math.pow(2, -55)的数会被认为是0(运算过程中只能计算55位数据),导致浮点数 + 小于Math.pow(2, -55)的数时,与自身相等是成立的。

补充2:
以上结论得出的精度Math.pow(2, -55) 仅适用于纯小数的运算。如下图则不同精度也会得出等式成立的结论,其原因其实我们早就看到了,根据IEEE745标准,Number类型为双精度 IEEE 754 ‘‘64 位’’ 浮点类型;重点就在这里的64位,除去指数位和符号位,用于表示数据的尾数位部分中,整数部分占据的位数越多,那么留给小数位部分的位数就越少,结果就是精度更低了。
在这里插入图片描述


三、解决思路

仅提供一个思路,这里就不介绍使用第三方库的方案了。

1. 设置最小误差值

可以使用Number.EPSILON作为最小误差值判断,或者随意设置一个数值常量。

2. 两数误差小于最小误差值即视为相等

代码如下(示例):

const epsilon = (a, b)=>{
	return Math.abs(a - b) < 设置的最小误差值
}

总结

js中浮点数运算的不相等问题本质是由于二进制数在js中采用IEEE745标准表示时的精度存在误差,导致运算中等式不一定成立的情况出现。

参考链接

MDN-Number: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Number
面试题:为什么0.1+0.2 !==0.3?如何实现等于0.3?:https://blog.csdn.net/qq_42033567/article/details/107753355

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值