js浮点数计算精度问题案例及解决方案

在项目中计算商品价格的时候再次遇到js浮点数计算出现误差的问题,以前一碰到这个问题就用tofixed方法进行处理一下,这对于一个程序员来说是及其不严谨的。
何况用tofixed方法也是有问题的

一、精度问题案例
console.log(0.1 + 0.2);//0.30000000000000004
console.log(1.0 - 0.9);//0.09999999999999998
console.log(19.9 * 100);//1989.9999999999998
console.log(6.6 / 0.2);//32.99999999999999
二、不精准原因:

下面我们来说一下浮点数运算产生误差的原因:(拿0.1+0.2=0.30000000000000004进行举例)

首先,我们要站在计算机的角度思考 0.1 + 0.2 这个看似小儿科的问题。我们知道,能被计算机读懂的是二进制,而不是十进制,所以我们先把 0.1 和 0.2 转换成二进制看看:

0.1 => 0.0001 1001 1001 1001…(无限循环)

0.2 => 0.0011 0011 0011 0011…(无限循环)

上面我们发现0.1和0.2转化为二进制之后,变成了一个无限循环的数字,这在现实生活中,无限循环我们可以理解,但计算机是不允许无限循环的,对于无限循环的小数,计算机会进行舍入处理。进行双精度浮点数的小数部分最多支持 52 位,所以两者相加之后得到这么一串 0.0100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 ,因浮点数小数位的限制而截断的二进制数字,这时候,我们再把它转换为十进制,就成了 0.30000000000000004。

三、解决方案
// 计算补充0数量
export const getZero = (a, b) => {
  let zero = '',
    len = 0;
  if (a > b) {
    len = a - b;
  } else {
    len = b - a;
  }
  for (let i = 0; i < len; i++) {
    zero += '0';
  }
  return zero;
};
// 浮点数转换整数
export const decimalToInteger = (a, b) => {
  let _a = a.toString(),
    _b = b.toString();
  if (!_a.match(/\./g) && !_b.match(/\./g)) {
    return {
      _a,
      _b,
      e: 1
    };
  } else {
    const al = _a.match(/\./g) ? _a.split('.')[1] : '',
      bl = _b.match(/\./g) ? _b.split('.')[1] : '',
      max = Math.max(al.length, bl.length),
      e = Math.pow(10, max);
    _b = _b.replace(/\./g, '');
    _a = _a.replace(/\./g, '');
    if (al.length > bl.length) {
      _b += getZero(al.length, bl.length);
    } else {
      _a += getZero(al.length, bl.length);
    }
    return {
      _a,
      _b,
      e
    };
  }
};
// 加
export const add = (a, b) => {
  const {_a, _b, e} = decimalToInteger(a, b);
  return (Number(_a) + Number(_b)) / e;
};
// 减
export const subtract = (a, b) => {
  const {_a, _b, e} = decimalToInteger(a, b);
  return (Number(_a) - Number(_b)) / e;
};
// 乘
export const multiply = (a, b) => {
  const {_a, _b, e} = decimalToInteger(a, b);
  return (Number(_a) * Number(_b)) / Math.pow(e,2);
};
// 除以
export const divide = (a, b) => {
  const {_a, _b, e} = decimalToInteger(a, b);
  return (Number(_a) / Number(_b));
};
四、调用方法
console.log(add(0.1, 0.2));//0.3
console.log(subtract(1.0, 0.9));//0.1
console.log(multiply(19.9, 100));//1990
console.log(divide(6.6, 0.2));//33`

你可能感兴趣的:(js浮点数计算精度问题案例及解决方案)