JavaScript货币计算乘100转整数时浮点精度异常排查
异常原因
JavaScript 中所有数值都遵循 IEEE 754 双精度浮点数存储标准,这类存储逻辑使用二进制分数记录数值,大量十进制小数无法被二进制精确表示——和十进制体系下无法精确写出1/3的有限小数形式是一个道理。
你看到只有2.05乘100出问题,不代表其他数值的计算没有误差:1.01、3.1这类值本身存储时就不是精确的十进制值,只是它们乘100之后的误差极小,小到控制台输出时自动舍入成了整数,肉眼看不到偏差而已。2.05本身的存储值比真实值略小,乘100后误差被同步放大得到204.99999999999997,这个偏差刚好超过了控制台默认的舍入阈值,才把精度问题暴露了出来。
无第三方库的解决方法
考核项目不允许引入外部库的场景下,用原生方法即可彻底规避这个问题:
- 所有浮点数乘100转分单位的逻辑,外层统一包裹
Math.round()做四舍五入取整,不要直接使用乘法结果。不要用parseInt处理这类转换,parseInt是字符串解析方法,对数字的处理逻辑存在额外边界问题,Math.round刚好可以把浮点数误差带来的微小偏差修正为正确整数。 - 转换完成后,所有金额相关的运算全程使用分单位的整数计算,不要在逻辑判断、数值比较环节切回元单位的浮点数,直到最终需要给用户展示结果时,再做除法转换为元单位格式,避免中间运算反复引入精度问题。
修正后的代码如下:
function checkCashRegister(price, cash, cid) { price = Math.round(price * 100); cash = Math.round(cash * 100); for (let i = 0; i < cid.length; i++) { cid[i][1] = Math.round(cid[i][1] * 100); } console.log(price); console.log(cash); console.log(cid); } console.log( checkCashRegister(19.23, 200, [["PENNY", 1.01], ["NICKEL", 2.05], ["DIME", 3.1], ["QUARTER", 4.25], ["ONE", 90], ["FIVE", 55], ["TEN", 20], ["TWENTY", 60], ["ONE HUNDRED", 100]]) );
运行上述代码后,NICKEL对应的值会正确输出为205,所有金额转换结果均符合预期。
内容的提问来源于stack exchange,提问作者Chris Andrew
相关产品推荐
相关产品推荐

