Javascript浮点数运算精度问题解决方案

125 阅读1分钟

遇到的问题

日环比计算方式:今日数据(小数形式)- 昨日数据(小数形式)

有时会出现下面问题:

const val = 1.91 - 1.11; // 0.7999999999999998

问题原因

要搞清楚float累加为什么会产生误差,必须先大致理解float在机器里怎么存储的,这里只介绍一下组成

ec7ab33b.png

由上图可知, 浮点数由: 符号位 + 指数位 + 尾数部分, 三部分组成。由于机器中都是由二进制存储的,那么一个10进制的小数如何表示成二进制。 例如: 8.25转成二进制为1000.01, 这是因为 1000.01 = 1*2^3 + 0*2^2 + 0*2^1 + 0*2^0 + 0*2^-1 + 2*2^-2 = 1000.01.

(2)float的有效位数是6-7位,这是为什么呢?因为位数部分只有23位,所以最小的精度为1*2^-2310^-610^-7 之间,接近 10^-7

那么为什么float累加会产生误差呢,主要原因在于两个浮点数累加的过程。

减法运算,转化成加法运算处理,同样存在上述问题。

解决方案

使用下面定制化 toFloat 函数,对出现小数运算的地方进行显示设置。

// 四舍五入修正代码
Number.prototype.toFloat = function(n) {
  n = n ? parseInt(n) : 0;
  if (n <= 0) {
    return Math.round(this);
  }
  let num = Math.round(this * Math.pow(10, n)) / Math.pow(10, n); //四舍五入
  num = Number(num).toFixed(n); //补足位数
  return Number(num);
};

故问题中描述的部分解决办法是:

const val = (1.91 - 1.11).toFloat(2); // 0.8

【参考资料】

  1. 计算机浮点数运算误差与解决误差的算法 - chenhuan001 - 博客园

  2. 关于计算机中整数和浮点数运算的一…_C/C++_月光轩辕的专栏-CSDN博客

  3. 计算机组成原理第4章 浮点数运算方法