js 实现一个完美的toFixed(四舍五入)

toFixed问题

在JavaScript中,我们经常需要对数字进行精确的处理,比如保留小数位数。而toFixed()函数正是JavaScript提供的一种用于保留小数位数的方法。然而,由于浮点数在计算机中的存储方式,toFixed()四舍五入会在一些特殊情况下出现异常。如:

// 返回2.5
2.55.toFixed(1)

解决方案

常用解决方案

网络上大部分解决方案是通过自己重写保留小数位数的函数来解决问题,代码如下

function toFixed(num, digits = 0) {
  return (Math.round(num * Math.pow(10, digits)) / Math.pow(10, digits)).toFixed(digits)
}
// 返回1.25
toFixed(1.255, 2)

但是,这些方法仍然无法做到完美的保留小数位数,也会遇到一些出现‘.999的情况’

完美解决方案

function toFixed(num, digits = 0) {
  let zeroStrNum = num.toString()
  
  // 处理科学计算情况
  if(zeroStrNum.includes("e")){
     const m = zeroStrNum.match(/\d(?:\.(\d*))?e([+-]\d+)/);
    zeroStrNum = num.toFixed(Math.max(0, (m[1] || '').length - m[2]))
  }
  
  let isNegativeNum = false
  // 判断是否为负数
  if(zeroStrNum.startsWith('-')){
    isNegativeNum = true
    zeroStrNum = zeroStrNum.slice(1)
  }
  // 获取小数点位置
  const dotIndex = zeroStrNum.indexOf('.')
  // 如果是整数/保留小数位数等于超过当前小数长度,则直接用toFixed返回
  if(dotIndex === -1 || (zeroStrNum.length - (dotIndex + 1) <= digits)){
    return num.toFixed(digits)
  }

  // 找到需要进行四舍五入的部分
  let numArr = zeroStrNum.match(/\d/g) || [];
  numArr = numArr.slice(0, dotIndex + digits + 1)
 
  // 核心处理逻辑
  if (parseInt(numArr[numArr.length - 1], 10) > 4) {
    // 如果最后一位大于4,则往前遍历+1
    for (let i = numArr.length - 2; i >= 0; i--) {
      numArr[i] = String(parseInt(numArr[i], 10) + 1);
      // 判断这位数字 +1 后会不会是 10
      if (numArr[i] === '10') {
        // 10的话处理一下变成 0,再次for循环,相当于给前面一个 +1
        numArr[i] = '0';
      } else {
        // 小于10的话,就打断循环,进位成功
        break;
      }
    }
  }
  // 将小数点加入数据
  numArr.splice(dotIndex, 0, ".")
  
  // 处理多余位数
  numArr.pop()
  
  // 如果事负数,添加负号
  if(isNegativeNum){
    numArr.unshift("-")
  }
  
  return Number(numArr.join('')).toFixed(digits)  
}

// 返回1.26
toFixed(1.255, 2)

附:
代码链接(自己写的工具函数库): github | npm
参考链接: link

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

A-wliang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值