JavaScript 中 Number.prototype.toFixed 方法五舍六入的问题

五舍六入的问题

JavaScript中 Number.prototype.toFixed 方法,可以将数字保留指定位数的小数,最后一位小数通过其后面一位“四舍五入”得到。这里之所以要将“四舍五入”打上引号,是因为在 Chrome/Firefox 上,这个方法根据小数位数的不同(或整数部分是否为0),表现的行为可能是四舍五入,也可能是“五舍六入”。是的,你没听错,“五舍六入”,看下下面这组 Chrome 上的例子:

describe('toFixed() test', function () {
    it('Number.toFixed()', function () {
        expect(1.35.toFixed(1)).toBe("1.4");                      // true            
        expect(1.335.toFixed(2)).toBe("1.34");                  // false
        expect(1.3335.toFixed(3)).toBe("1.334");              // false
        expect(1.33335.toFixed(4)).toBe("1.3334");          // true
        expect(1.333335.toFixed(5)).toBe("1.33334");      // false
        expect(1.3333335.toFixed(6)).toBe("1.333334");  // false
    });
});

上面代码中的有部分最后一个位是 5,却被舍弃掉了,即“五舍六入”。这会导致页面中,有些显示小数的地方出现最后一位与预期不符的情况。这种情况在 IE6-IE10 上没出现,但是在 Chrome44/Firefox41 上出现了。

修复方式

这篇文章提供了两种修复方法,一起看一看:

第一种修复方式:判断最后一位小数为 5 的,改成 6, 再调用 toFixed。例如 1.335(调用 toFixed(2) 结果为 1.33)先修改为 1.336,在调用 toFixed(2) ,得到的结果是 1.34,符合预期。

function toFixed(num, fractionDigits) {
    var str = num + ''
    var len = str.length
    var last = str.substr(len-1, len)
    if (last == '5') {
        last = '6'
        str = str.substr(0, len-1) + last
        return Number(str).toFixed(fractionDigits)       
    } else {
        return num.toFixed(fractionDigits)
    }
}

第二种修复方式:将数字先进行放大指定倍数,使最后一位小数显示成个位数,然后加上一个 0.5 后,使最后一位进位(如果最后一位大于等于 5),再调用 parsInt() 去尾并缩小相同倍数。例如 1.335 放大 100 倍后是 133.5,加上 0.5 后是 134.0,调用 parseInt() 得到 134,再缩小 100 倍得到 1.34,结果符合预期。

function toFixed(num, fractionDigits) {
    var times = Math.pow(10, fractionDigits)
    var des = num * times + 0.5
    des = parseInt(des, 10) / times
    return String(des)
}

上面两种方式都可以修复“五舍六入”的问题,但是实现方法和计算复杂度都稍大,下面我提供一种简单的方法。

第三种修复方式:在调用 toFixed() 方法前,先给数字补一个极小值,然后再调用 toFixed() 方法。例如 1.335 加上一个极小值(0.00000001)后是 1.33500001,然后调用 toFixed(2)得到 1.34,结果符合预期。这个极小值只要不影响最后结果,可以根据要保留的小数位数决定取多少,但也不能太小了(例如 1.3350000001.toFixed(2) 结果是 1.34,而 1.33500000000000001.toFixed(2) 结果是 1.33)。

function toFixed(num, fractionDigits) {
    return (num + 0.00000001).toFiexed(fractionDigits);
}

使用第三种方式修复后,再次运行测试代码,都可以得到正确的结果:

describe('toFixed() test', function () {
    it('myToFixed()', function () {
        var oldToFixed = Number.prototype.toFixed;
        // 这里重写原生方法是为了方便比较,不推荐这样做
        Number.prototype.toFixed = function (n) {                   
            return oldToFixed.call(this + 0.00000001, n);
        }
        expect(1.35.toFixed(1)).toBe("1.4");
        expect(1.335.toFixed(2)).toBe("1.34");
        expect(1.3335.toFixed(3)).toBe("1.334");
        expect(1.33335.toFixed(4)).toBe("1.3334");
        expect(1.333335.toFixed(5)).toBe("1.33334");
        expect(1.3333335.toFixed(6)).toBe("1.333334");
        Number.prototype.toFixed = oldToFixed;
    });
});

参考

转载于:https://my.oschina.net/u/1756807/blog/777893

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值