likes
comments
collection
share

js精度丢失的问题,重新封装toFixed()

作者站长头像
站长
· 阅读数 19

js精度丢失的问题,重新封装toFixed()

最近项目中遇到一个问题,那就是用tofixed()保留位小数的时候出现问题;比如2.55.tofixed(1)的结果是2.5。在网上搜了以什么是什么toFixed用的是银行算法,大致了解了一下银行家算法,意思就是四舍五入的话,如的情况有五种,舍的情况只有四种,所以5看情况是舍还是入。

然而事实并不是什么银行家算法,而是计算的二进制有关,计算机在存储数据是以二进制的形式存储的整数存储倒是没有问题的,但是小数就容易出问题了,就比如0.1的二进制是0.0001100110011001100110011001100110011001100110011001101...无限循环的但是计算保存的时候肯定是有长度限制的,到你使用的时候计算会做一个近似处理

如下图:

js精度丢失的问题,重新封装toFixed() 那我再看一下2.55的是啥样子的吧

js精度丢失的问题,重新封装toFixed() 现在是不是很容易理解了为什么2.55保留一位小数是2.5而不是2.6了吧

同时计算机在保留二进制的时候也会存在进位和舍去的 所以这也是解释了一下为什么0.1+0.2不等于0.3,因为0.1和0.2本来存储的就不是精确的数字,加在一起就把误差放大了,计算就不知道你是不是想要的结果是0.3了,但是同样的是不精确是数字加一起确实正确的就比如0.2和0.3

见下图:

js精度丢失的问题,重新封装toFixed() 这是为什么呢,因为计算存储的时候有进有啥,一个进一个舍两个相加就抵消了。

知道原因了该怎么解决呢?

那就是不用数字,而是用字符串。如果你涉及到一些精确计算的话可以用到一些比较成熟的库比如math.js或者# decimal.js。我今天就是对toFixed()重新封装一下,具体思路就是字符串加整数之间的运算,因为整数存储是精确(不要扛啊 不要超出最大安全整数)


export function toFixed(num, fixed = 2) {//fixed是小数保留的位数
  let numSplit = num.toString().split('.');
  if (numSplit.length == 1 || !numSplit[1][fixed] || numSplit[1][fixed] <= 4) {
    return num.toFixed(fixed);
  }
 function toFixed(num, fixed = 2) {
        let numSplit = num.toString().split(".");
        if (
          numSplit.length == 1 ||
          !numSplit[1][fixed] ||
          numSplit[1][fixed] <= 4
        ) {
          return num.toFixed(fixed);
        }
        numSplit[1] = (+numSplit[1].substring(0, fixed) + 1 + "").padStart( fixed,0);
        if (numSplit[1].length > fixed) {
          numSplit[0] = +numSplit[0] + 1;
          numSplit[1] = numSplit[1].substring(1, fixed + 1);
        }
        return numSplit.join(".");
      }
  if (numSplit[1].length > fixed) {
    numSplit[0] = +numSplit[0] + 1;
    numSplit[1] = numSplit[1].substring(1, fixed + 1);
  }
  return numSplit.join('.');
}

文章样式简陋,但是干货满满。说的不对的,希望大家指正。