如何让 0.1 + 0.2 === 0.3 ?

139 阅读1分钟

不相等的原因总结:

  • 进制转换 :js 在做数字计算的时候,0.1 和 0.2 都会被转成二进制后无限循环 ,但是 js 采用的 IEEE 754 二进制浮点运算,最大可以存储 53 位有效数字,于是大于 53 位后面的会全部截掉,将导致精度丢失。
  • 对阶运算 :由于指数位数不相同,运算时需要对阶运算,阶小的尾数要根据阶差来右移(0舍1入),尾数位移时可能会发生数丢失的情况,影响精度。

解决办法:

转为整数(大数)运算。

function add(a, b) {
  const maxLen = Math.max(
    a.toString().split(".")[1].length,
    b.toString().split(".")[1].length
  );
  const base = 10 ** maxLen;
  const bigA = BigInt(base * a);
  const bigB = BigInt(base * b);
  const bigRes = (bigA + bigB) / BigInt(base); // 如果是 (1n + 2n) / 10n 是等于 0n的。。。
  return Number(bigRes);
}

使用 Number.EPSILON 误差范围。

Number.EPSILON 的实质是一个可以接受的最小误差范围,一般来说为 Math.pow(2, -52) 。

function isEqual(a, b) {
  return Math.abs(a - b) < Number.EPSILON;
}

console.log(isEqual(0.1 + 0.2, 0.3)); // true

转成字符串,对字符串做加法运算。

// 字符串数字相加
var addStrings = function (num1, num2) {
  let i = num1.length - 1;
  let j = num2.length - 1;
  const res = [];
  let carry = 0;
  while (i >= 0 || j >= 0) {
    const n1 = i >= 0 ? Number(num1[i]) : 0;
    const n2 = j >= 0 ? Number(num2[j]) : 0;
    const sum = n1 + n2 + carry;
    res.unshift(sum % 10);
    carry = Math.floor(sum / 10);
    i--;
    j--;
  }
  if (carry) {
    res.unshift(carry);
  }
  return res.join("");
};

function isEqual(a, b, sum) {
  const [intStr1, deciStr1] = a.toString().split(".");
  const [intStr2, deciStr2] = b.toString().split(".");
  const inteSum = addStrings(intStr1, intStr2); // 获取整数相加部分
  const deciSum = addStrings(deciStr1, deciStr2); // 获取小数相加部分
  return inteSum + "." + deciSum === String(sum);
}

console.log(isEqual(0.1, 0.2, 0.3)); // true