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)