toFixed()四舍五入精度丢失问题及解决办法
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
let num = 8.885
num = num.toFixed(2)
console.log(num) => 8.88
按理说四舍五入是8.89但是实际结果为8.88。
但是实际上采用的银行家舍入法四舍六入五考虑五后非零进1五后为零看奇偶五前为偶舍去五前为奇进一。
四舍五入推荐使用round()方法,如果一定要使用toFixed方法就会引起以下问题
js - tofixed精度丢失问题
toFixed() 方法可把 Number 四舍五入为指定小数位数的数字。例如将数据Num保留2位小数则表示为toFixed(Num)
但是其四舍五入的规则与数学中的规则不同使用的是银行家舍入规则
银行家舍入所谓银行家舍入法其实质是一种四舍六入五取偶又称四舍六入五留双法。具体规则如下简单来说就是四舍六入五考虑五后非零就进一五后为零看奇偶五前为偶应舍去五前为奇要进一。
经测试发现在chorme下面并没有完全遵守这个规则尤其是5的后面没有数字的时候不是这么判断的如下
var b = 1.335
b.toFixed(2)
"1.33"
var b = 1.345
b.toFixed(2)
"1.34"
var b = 1.355
b.toFixed(2)
"1.35"
var b = 1.365
b.toFixed(2)
"1.36"
var b = 1.375
b.toFixed(2)
"1.38"
var b = 1.385
b.toFixed(2)
"1.39"
可以发现在chorme下没有完全去遵循这个规律或许它有自己的算法但是毕竟它没有遵循通用的银行家算法所以tofixed这个方法在涉及到金钱计算的业务中还是少用.
总而言之不论引入toFixed解决浮点数计算精度缺失的问题也好它有没有使用银行家舍入法也罢都是为了解决精度的问题但是又离不开二进制浮点数的环境但至少他帮助我们找到了问题所在从而让我们有解决方法。
一开始的办法是把要四舍五入的后一位单独拎出来单独判断。
解决办法,重写toFixed()方法
// toFixed兼容方法
Number.prototype.toFixed = function (n) {
if (n > 20 || n < 0) {
throw new RangeError('toFixed() digits argument must be between 0 and 20');
}
const number = this;
if (isNaN(number) || number >= Math.pow(10, 21)) {
return number.toString();
}
if (typeof (n) == 'undefined' || n == 0) {
return (Math.round(number)).toString();
}
let result = number.toString();
const arr = result.split('.');
// 整数的情况
if (arr.length < 2) {
result += '.';
for (let i = 0; i < n; i += 1) {
result += '0';
}
return result;
}
const integer = arr[0];
const decimal = arr[1];
if (decimal.length == n) {
return result;
}
if (decimal.length < n) {
for (let i = 0; i < n - decimal.length; i += 1) {
result += '0';
}
return result;
}
result = integer + '.' + decimal.substr(0, n);
const last = decimal.substr(n, 1);
// 四舍五入转换为整数再处理避免浮点数精度的损失
if (parseInt(last, 10) >= 5) {
const x = Math.pow(10, n);
result = (Math.round((parseFloat(result) * x)) + 1) / x;
result = result.toFixed(n);
}
return result;
}