浮点数精度计算问题

242 阅读2分钟

1.背景

console.log(0.1 + 0.2) // 0.30000000000000004
console.log(0.4 - 0.1) // 0.30000000000000004
console.log(35.41 * 100) // 3540.9999999999995
console.log(1.02 / 5) // 0.20400000000000001

上述计算可得出结论,js计算中只要出现浮点数,就有可能出现 计算精度问题

2.思路

网上优秀的思路有很多,这里不一一说明。
我们的思路是,既然浮点数会导致精度问题,我们解决浮点数问题就好了。

  • 获取参与计算的数字的小数点长度,以及一些其他所需的基础数据
  • 将两数小数进行移位处理,注意不是直接乘10/100/100,而是利用转化为string去掉'.',再转化为number
  • 针对每个计算方法特殊处理

3.代码实现

// 处理小数数据
const handlingDecimals = (x = 0, y = 0) => {
  let lx, // x 小数点长度
      ly, // y 小数点长度
      lMax, // 最长小数点长度
      strx, // x 字符串
      stry, // y 字符串
      intx, // x 去除小数点后的整数值
      inty, // y 去除小数点后的整数值
      divx, // x 转化为整数时扩大的倍数
      divy, // y 转化为整数时扩大的倍数
      divMax, // 最大转化为整数时扩大的倍数
      divSum, // 乘法时总共扩大的倍数
      divGap // 除法时两数相差倍数 要注意这里是计算 l =  l被除数 - l除数
  try {
    strx = x.toString()
    lx = strx.split('.')[1].length
  } catch {
    strx = 0
    lx = 0
  }
  try {
    stry = y.toString()
    ly = stry.split('.')[1].length
  } catch {
    stringy = 0
    ly = 0
  }
  lMax = Math.max(lx, ly)
  intx = Number(strx.replace('.', ''))
  inty = Number(stry.replace('.', ''))
  divx = Math.pow(10, lx)
  divy = Math.pow(10, ly)
  divMax = Math.pow(10, lMax)
  divSum = Math.pow(10, lx+ly)
  divGap = Math.pow(10, ly - lx)

  return {
    lx, ly, lMax, divx, divy, divMax, divSum, divGap, strx, stry, intx, inty
  }
}

// 乘 (小数转化为整数公共方法)
const multiply = (x = 0, y = 0) => {
  const { intx, inty, divSum } = handlingDecimals(x, y)
  return intx * inty / divSum
}

// 加
const addition = (x = 0, y = 0) => {
  const { divx, divy, divMax } = handlingDecimals(x, y)
  return (multiply(x, divx) + multiply(y, divy)) / divMax
}

// 减
const subtraction = (x = 0, y = 0) => {
  const { divx, divy, divMax } = handlingDecimals(x, y)
  return (multiply(x, divx) - multiply(y, divy)) / divMax
}

// 除
const division = (x = 0, y = 0) => {
  const { intx, inty, divGap } = handlingDecimals(x, y)
  return multiply(intx/inty, divGap)
}

4.测试用例

const resAddition = addition(0.1, 0.2) // 0.3
const resSubtraction = subtraction(0.4, 0.1) // 0.3
const resMultiply = multiply(35.41, 100) // 3541
const resDivision = division(1.02, 5) // 0.204

后续TODO

  • 保留X位小数
  • 函数封装&异常处理