1.JS 浮点数精度问题
由于 JS 存在精度问题,所以以下运算结果与预期不符。
注:以下代码若无特殊说明,则均为谷歌浏览器下的运行结果。
0.56 * 100 // 56.00000000000001
0.1 + 0.2 // 0.30000000000000004
0.3 - 0.1 // 0.19999999999999998
所以不要用浮点数做以下判断
if (0.1 + 0.2 === 0.3) {}
当我们对浮点数做运算后要保留固定位小数时,也要进行处理
0.5623 * 100 // 预期 56.23, 实际 56.230000000000004
// 为预防此类情况, 需再做个四舍五入
(0.5623 * 100).toFixed(2) // 56.23
2.toFixed 的 bug
但这里要注意,toFixed
方法有 bug。比如
(12.245).toFixed(2) // 预期 12.25, 实际 12.24
所以需要重写 toFixed
方法,这里我们不选择覆盖原有 toFixed
,另定义一个 toFixed2
/**
* 重写 toFixed 方法
* @param {Number} d 保留的小数位数
*/
Number.prototype.toFixed2 = function (d) {
let s = this + '';
if (!d) d = 0;
if (s.indexOf('.') == -1) s += '.';
s += new Array(d + 1).join('0');
if (new RegExp('^(-|\\+)?(\\d+(\\.\\d{0,' + (d + 1) + '})?)\\d*$').test(s)) {
let s = '0' + RegExp.$2,
pm = RegExp.$1,
a = RegExp.$3.length,
b = true;
if (a == d + 2) {
a = s.match(/\d/g);
if (parseInt(a[a.length - 1]) > 4) {
for (let i = a.length - 2; i >= 0; i--) {
a[i] = parseInt(a[i]) + 1;
if (a[i] == 10) {
a[i] = 0;
b = i != 1;
} else break;
}
}
s = a.join('').replace(new RegExp('(\\d+)(\\d{' + d + '})\\d$'), '$1.$2');
}
if (b) s = s.substr(1);
return (pm + s).replace(/\.$/, '');
}
return this + '';
};
代码参考于:Js toFixed()四舍五入BUG的解决方法
再测试一下
(12.245).toFixed2(2) // 12.25
3.toFixed 的更多思考
关于 toFixed
进行舍入的奇怪现象,有人说它采用的不是四舍五入
,而是银行家舍入
。
银行家舍入
银行家舍入
的介绍可见百度百科:银行家舍入
简单理解为:四舍六入五考虑,五后非空就进一,五后为空看奇偶,五前为偶应舍去,五前为奇要进一
按这个思路,进行以下测试
(12.205).toFixed(2) // 预期 '12.21' 实际 '12.21'
(12.215).toFixed(2) // 预期 '12.22' 实际 '12.21'
(12.225).toFixed(2) // 预期 '12.22' 实际 '12.22'
(12.235).toFixed(2) // 预期 '12.24' 实际 '12.23'
(12.245).toFixed(2) // 预期 '12.24' 实际 '12.24'
(12.255).toFixed(2) // 预期 '12.26' 实际 '12.26'
(12.265).toFixed(2) // 预期 '12.26' 实际 '12.27'
(12.275).toFixed(2) // 预期 '12.28' 实际 '12.28'
(12.285).toFixed(2) // 预期 '12.28' 实际 '12.29'
(12.295).toFixed(2) // 预期 '12.30' 实际 '12.29'
可以发现,toFixed
并不遵循银行家舍入
。而且在不同浏览器下运行的结果并不一致,比如在 IE 下是符合四舍五入
规则的。
这里我们在谷歌下运行下面的代码,能发现一个有趣的现象
(2.245).toFixed(2) // 2.25
(12.245).toFixed(2) // 12.24
(112.245).toFixed(2) // 112.25
(1112.245).toFixed(2) // 1112.24
(11112.245).toFixed(2) // 11112.25
(111112.245).toFixed(2) // 111112.24
4.常用保留小数点舍入方法
- Math.round(),四舍五入取整
- Math.floor(),向下取整
- Math.ceil(),向上取整
// 保留两位小数的写法
Math.round(12.245 * 100) / 100 // 12.25
Math.floor(12.245 * 100) / 100 // 12.24
Math.ceil(12.245 * 100) / 100 // 12.25