js 计算精度问题

一 浮点计算精读出现的问题枚举

0.1 + 0.2               // 0.30000000000000004  not  0.3
0.3 - 0.2             // 0.09999999999999998    not 0.1
1.005 * 100         //100.49999999999999  not  100.5 , 所以用 Math.round(1.005*100)/100 得到的也是1 not  1.01
0.69 / 10           // 0.06899999999999999  not 0.069
1.005.toFixed(2)  // '1.00' not '1.01' (四舍五入:采用银行家算法 - 不准确)

二 js 浮点计算 精度出现精读问题的原因

js所有数字包括整数浮点数只有一种类型 - Number类型,它遵循IEEE 754标准,使用固定的64位固定长度来表示,也就是标准的double 双精度浮点数。(单精读 是float 32位) 。
这样的存储结构优点是可以归一化处理整数和小数,节省存储空间。
  64位比特又可分为三个部分:

符号位S:第 1 位是正负数符号位(sign),0代表正数,1代表负数

指数位E:中间的 11 位存储指数(exponent),用来表示次方数

尾数位M:最后的 52 位是尾数(mantissa),超出的部分自动进一舍零

所以产生误差的原因:由于有一些浮点数用二进制表示时是无穷的,当浮点数转化为二进制进行存储时,会把超过53位之后的进行合并导致精读丢失。

三 js 浮点运算解决方案 (保留多少位数小数,末尾实现四舍五入)

方法一: 自定义函数
    function toFixed(n, d) {
      if (n > Number.MAX_SAFE_INTEGER || n < Number.MIN_SAFE_INTEGER) {
        throw new Error('超过js安全计算值范围,此函数暂不支持!');
      }
      const radix = 10 ** d;
      let num = math.floor(n * radix);
      const a = String(n).split('.');
      if (a[1] && a[1].length >= d) {
        if (Number(a[1][d]) >= 5) {
          num += 1;
        }
      }
      return num / radix;
    }
方法二 利用mathjs 库
import { create, all, round } from 'mathjs';

const config = {
  number: 'BigNumber',
  precision: 64,
};

const mathJs = create(all, config);
// number 和 bignumber类型
// mathJs.evaluate('0.1 + 0.2');  // 0.3
// 默认保留两位小数,实现四舍五入

export const math = (expression, precision = 2) => {
  return round(mathJs.number(mathJs.evaluate(expression)), precision);
};

math('0.1 + 0.2'); // 0.3
原文地址:https://www.cnblogs.com/honkerzh/p/14551197.html