js精度丢失原因及解决方案

285 阅读2分钟

起因

近期在项目中有出现后端返回大数值,一使用就会被js改写值,因为它超出了js的最大安全数。

可以看到这个数值大于 Number.MAX_SAFE_INTEGER 就不被保证正确了。

需求

  1. 通过 AJAX 获取后端返回的值正确显示;
  2. 通过换算得到正确结果;

正确显示返回值

后端返回数据类型为数字并且数值大于安全值会导致值显示错误,需后端返回字符串类型即可解决取值错误问题。

通过换算得到正确结果

思路

  • 有的同学肯定会说直接相加不就行了吗?答案肯定是不行的,转为数字就会因为超出安全范围而得到错误的结果。

  • 所以我想到用循环字符串使其个位相加、百位相加、千位...以此类推。

  • 每一个商品的单价长度会不一致,如果直接进行循环相加会导致个位、十位不能正常对齐,所以在求和前我们需要对所有数值进行补0操作,已确保他们的位数一致。

  • 最后我们就可以进行循环求和进位换算了。

const res = [{
  "name": "房子",
  "price": "9999999999999999"
}, {
  "name": "装修",
  "price": "108"
}]

// 得出最长位数用于补0和循环求和
let maxLen = res.reduce((prev, current) => {
  if (!prev || prev < current.price.length) prev = current.price.length

  return prev
}, 0),
  // 总和
  total = '',
  // 储存余数用于进位
  remaining = 0

// 计算结果前先进行补位
res.forEach(item => {
  if (item.price.length < maxLen) {
    [...new Array(maxLen - item.price.length).keys()].forEach(() => {
      item.price = '0' + item.price
    })
  }
})

// 计算得出结果
for (let i = maxLen - 1; i > -1; i--) {
  // 每一位相加得出的结果
  const n = res.reduce((prev, current) => prev + Number(current.price[i]), remaining),
    // 个位数值
    bits = n % 10

  // 余数进位
  remaining = (n - bits) / 10;

  // 个位数值
  total = bits.toString() + total
}

// 如果进位值大于 0
if (remaining) total = remaining.toString() + total

思路

  • 采用内置对象 BigInt 进行换算
const res = [{  "name": "房子",  "price": "9999999999999999"}, {  "name": "装修",  "price": "19878763"}]

// 使用BigInt
res.reduce((prev, current) => {
  return BigInt(current.price) + prev
}, BigInt(0)).toString()