1、tofixed方法
toFixed()
方法可把 Number 四舍五入为指定小数位数的数字。
例如:将数据Num保留2位小数,则表示为:Num.toFixed(2);
,但是其四舍五入的规则与数学中的规则不同,使用的是银行家舍入规则
。所谓银行家舍入法,其实质是一种四舍六入五取偶(又称四舍六入五留双)法。
简单来说就是:四舍六入五考虑,五后非零就进一,五后为零看奇偶,五前为偶应舍去,五前为奇要进一。
银行家舍入法是由 IEEE 754 标准规定的浮点数取整算法,大部分的编程软件都使用这种方法。具体规则如下:
当舍去位
的数值:
- 小于等于4,直接舍去该位
- 大于等于6,向前位进一
- 等于5
- 5后有数,向前位进一
- 5后全0
- 5前位数值为奇,则向前位进一(将前位凑成偶)
- 5前位数值为偶,则直接舍去该位
显然这种规则不符合我们平常在数据中处理的方式。经过大量测试,对于弱类型的js在实际测试中也不能完全和上面的规则保持一致,让我变得十分困惑。
image.png
2、Math.round方法
Math.round()
方法可把一个数字舍入为最接近的整数。
例如:Math.round(x)
,则是将x取其最接近的整数。其取舍的方法使用的是四舍五入中的方法,符合数学中取舍的规则。对于小数的处理没有那么便捷,但是可以根据不同的要求,进行自定义的处理。
例如:对于X进行保留两位小数的处理,则可以使用Math.round(X * 100) / 100
进行处理。
这个必须保证小数位后至少有两位小数,如果不够自动补零,该方法就需要改进一下
// 方法一:Math.round
function round(number, precision) {
return +(Math.round(+number + 'e' + precision) + `e-${precision}`)
}
// 方法二: 截取
function round(number, presision) {
const [int, decimals] = String(number).split('.');
let precisionDecimals = +decimals.slice(0, presision);
if (decimals[presision] > 4) precisionDecimals++;
return parseFloat(int + '.' + precisionDecimals)
}
3、封装方法
// num为传入的值,n为保留的小数位
function fomatFloat(num,n){
var f = parseFloat(num);
if(isNaN(f)){
return false;
}
f = Math.round(num *Math.pow(10, n))/Math.pow(10, n); // n幂
var s = f.toString();
var rs = s.indexOf('.');
//判定如果是整数,增加小数点再补0
if(rs < 0){
rs = s.length;
s += '.';
}
while(s.length <= rs + n){
s += '0';
}
return s;
}
复制代码
最后
js进行浮点运算的确非常不准确,包括上面的封装函数也并不能百分百返回的是准确的,比如下面
fomatFloat(134041.175,2); //134041.17
复制代码
为什么会这样呢?
经过测试发现
134041.175 * 100 //13404117.499999998
复制代码
然后我尝试使用三方库math.js
,进行测试
math.chain(134041.175).multiply(100); // {value: 13404117.499999998}
复制代码
结果不令人满意
不进行浮点运算的话,是可以的
Math.round(13404117.5); //13404118
复制代码
Math.round
本身没有问题,但是浮点运算产生了问题,这个也解释了上面为什么整数位数字的增减也会影响四舍五入的精度问题,所以如果涉及到金额对精度要求比较高的话,还是让后台返回正确的结果吧