浅析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%测试全覆盖,代码可读性强,不妨在你的应用里用起来!

posted @ 2018-02-28 20:23  古兰精  阅读(1073)  评论(0编辑  收藏  举报