今天遇到一个坑,是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!




