toFixed()四舍五入精度丢失问题及解决办法

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;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

cc980302

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

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

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

打赏作者

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

抵扣说明:

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

余额充值