浅析Number.EPSILON及JavaScript中的数值误差问题
一、Number.EPSILON 是什么
Number.EPSILON
属性表示 1 和大于 1 的最小值(可表示为 Number
)的差值。可直接使用 Number.EPSILON
来访问这个静态属性。
Number.EPSILON
属性的属性特性:Writable、Enumerable、Configurable 均为 no。
EPSILON
属性的值接近于 2.2204460492503130808472633361816E-16
,或者 2-52。
// Polyfill
if (Number.EPSILON === undefined){
Number.EPSILON = Math.pow(2, -52);
}
二、JS中的数值误差问题
我们都知道JavaScript中0.1+0.2不等于0.3,那怎么解决呢?
一般的解决方案就是说:把计算数字提升10的N次方成为一个整数,2个整数进行计算就不会有误差了,一般都用 1000 就行了,如下:
(0.1*1000+0.2*1000)/1000==0.3
//true
这里就不说了,今天主要是介绍ES6中的Number.EPSILON。
我们主要看这个浮点数的本质:就是将浮点数转换成了用二进制表示的最接近的近似值。
解决办法:ES6中Number.EPSILON提供了一种解决办法。ES6 在Number对象上面,新增一个极小的常量Number.EPSILON。根据规格,它表示 1 与大于 1 的最小浮点数之间的差。
对于 64 位浮点数来说,大于 1 的最小浮点数相当于二进制的1.00…001,小数点后面有连续 51 个零。这个值减去 1 之后,就等于 2 的 -52 次方。
Number.EPSILON === Math.pow(2, -52)
Number.EPSILON实际上是 JavaScript 能够表示的最小精度。误差如果小于这个值,就可以认为已经没有意义了,即不存在误差了。
引入一个这么小的量的目的,在于为浮点数计算,设置一个误差范围。我们知道浮点数计算是不精确的。因此,Number.EPSILON的实质是一个可以接受的最小误差范围。
// 这里做一个 Number.EPSILON 是否存在,存在最好,不存在则重新定义
if (!Number.EPSILON) {
Number.EPSILON = Math.pow(2,-52)
}
// 判断实际值和计算值是否相等(即是否在误差范围内)
function eqaul(n1,n2){
return Math.abs(n1-n2)<Number.EPSILON
}
console.log(eqaul(0.1+0.2,0.3))
最后推荐一个库,遇到浮点数误差问题时可以直接使用:https://github.com/dt-fe/number-precision,完美支持浮点数的加减乘除、四舍五入等运算。非常小只有1K,远小于绝大多数同类库(如Math.js、BigDecimal.js),100%测试全覆盖,代码可读性强,不妨在你的应用里用起来!