JS中的浮点型计算

今天遇到一个坑,是JS中 toFixed() 方法的坑,一直以为这个方法是“四舍五入”, 今天才发现不完全是。

在IE 11中测试结果和正常的“四舍五入”没有什么区别:

Number(3.315).toFixed(2)
"3.32"
Number(3.325).toFixed(2)
"3.33"
Number(3.335).toFixed(2)
"3.34"
Number(3.345).toFixed(2)
"3.35"
Number(3.355).toFixed(2)
"3.36"
Number(3.365).toFixed(2)
"3.37"
Number(3.375).toFixed(2)
"3.38"
Number(3.385).toFixed(2)
"3.39"
Number(3.395).toFixed(2)
"3.40"

但是在 Chrome 和 Firefox 下的结果却让我意外,它不完全符合“四舍五入”的规则,

Number(3.315).toFixed(2)
"3.31"
Number(3.325).toFixed(2)
"3.33"
Number(3.335).toFixed(2)
"3.33"
Number(3.345).toFixed(2)
"3.35"
Number(3.355).toFixed(2)
"3.35"
Number(3.365).toFixed(2)
"3.37"
Number(3.375).toFixed(2)
"3.38"
Number(3.385).toFixed(2)
"3.38"
Number(3.395).toFixed(2)
"3.40"


一脸懵逼啊,百度得到一个说法:

toFixed它是一个四舍六入五成双的诡异的方法(也叫银行家算法),"四舍六入五成双"含义:对于位数很多的近似数,当有效位数确定后,其后面多余的数字应该舍去,只保留有效数字最末一位,这种修约(舍入)规则是“四舍六入五成双”,也即“4舍6入5凑偶”这里“四”是指≤4 时舍去,"六"是指≥6时进上,"五"指的是根据5后面的数字来定,当5后有数时,舍5入1;当5后无有效数字时,需要分两种情况来讲:①5前为奇数,舍5入1;②5前为偶数,舍5不进。(0是偶数) 


没办法了,踩了这个坑只能自己来填了,自己实现四舍五入:


var Float = {
    toFixed: function (num, precision) {
        var times = Math.pow(10, precision == null ? 0 : precision);
        return (function (num) {return num.toFixed(precision)})(Math.floor(num * times + 0.5) / times);
    }
}


对于数据的运算,计算机会把数值转为二进制进行计算,而在转换过程中可能出现精度问题,例如 0.012 + 0.001 = 0.013,但是JS运算结果却与我们期望的 0.013 有些出入:


0.012 + 0.001
0.013000000000000001
为了处理这类问题,通常是将小数计算转为整数计算,看代码吧:



var Float = {

    /**
     * 四舍五入
     * @param num
     * @param precision
     * @returns {string}
     */
    toFixed: function (num, precision) {
        var times = Math.pow(10, precision == null ? 0 : precision);
        return (function (num) {return num.toFixed(precision)})(Math.floor(num * times + 0.5) / times);
    },

    /**
     * 加法
     * @returns {number}
     */
    add: function () {
		var maxTimes = 1;
		var integers = [];
		var i, item, sum;
		
        for (i = 0; i < arguments.length; i++) {
            item = Float._toInteger(arguments[i]);
			integers.push(item);
			maxTimes = Math.max(maxTimes, item.times);
        }
		
		sum = 0;
		for (i = 0; i < integers.length; i++) {
			sum += integers[i].value * maxTimes / integers[i].times;
		}
        return sum / maxTimes;
    },

    /**
     * 减法
     * @param n1
     * @param n2
     * @returns {number}
     */
    subtract: function (n1, n2) {
        var i1 = Float._toInteger(n1);
        var i2 = Float._toInteger(n2);
		if (i1.times > i2.times) {
			return (i1.value - i2.value * i1.times / i2.times) / i1.times;
		} else {
			return (i1.value * i2.times / i1.times - i2.value) / i2.times;
		}
    },

    /**
     * 乘法
     * @returns {number}
     */
    multiply: function () {
        var values = 1;
        var times = 1;
        var item;
        for (var i = 0; i < arguments.length; i++) {
            item = Float._toInteger(arguments[i]);
            values *= item.value;
            times *= item.times;
        }
        return values / times;
    },

    /**
     * 除法
     * @param n1
     * @param n2
     */
    divide: function (n1, n2) {
        var i1 = Float._toInteger(n1);
        var i2 = Float._toInteger(n2);
		if (i1.times == i2.times) {
			return i1.value / i2.value;
		} else if (i1.times > i2.times) {
			return (i1.value / i2.value) / (i1.times / i2.times);
		} else {
			return (i1.value / i2.value) * (i2.times / i1.times);
		}
    },

    _toInteger: function (num) {
        var str = num + "";
        var index = str.indexOf(".");
        return {
            value: parseInt(str.replace(".", "")),
            times: Math.pow(10, index < 0 ? 0 : str.substring(index + 1).length)
        };
    }
}

// Test
console.log("Float.toFixed(3.335, 2): ", Float.toFixed(3.335, 2));
console.log("Float.add(0.011, 0.001, 0.001): ", Float.add(0.011, 0.001, 0.001));
console.log("Float.subtract(0.014, 0.001): ", Float.subtract(0.014, 0.001));
console.log("Float.multiply(0.13, 0.1): ", Float.multiply(0.13, 0.1));
console.log("Float.divide(0.0013, 0.1): ", Float.divide(0.0013, 0.1));



Over!






内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.jsunw.com/?post=38

相关推荐

你肿么看?

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。