本文分析JavaScript中浮点数运算精度丢失的根本原因,指出其源于IEEE-754标准及二进制表示的局限性。通过对比测试展示常见计算误差,并提供基于toFixed()和parseFloat()的精度修正方案,以及封装isEqual工具方法,确保数值比较的准确性。
浮点数精度问题分析
分析
JavaScript 仅包含 Number 一种数字类型,且所有数值均遵循 IEEE-754 标准格式进行存储。需要注意的是,浮点数精度问题并非 JavaScript 独有,其根本原因在于部分小数在二进制表示下位数是无限的:
十进制 二进制 0.1 0.0001 1001 1001 1001 ... 0.2 0.0011 0011 0011 0011 ... 0.3 0.0100 1100 1100 1100 ... 0.4 0.0110 0110 0110 0110 ... 0.5 0.1 0.6 0.1001 1001 1001 1001 ...
因此,程序无法精确表示如 1.1 这样的数值,只能达到一定程度的近似,这导致了不可避免的精度丢失:
1.09999999999999999
在 JavaScript 中,情况可能更为复杂。以下数据基于 Chrome 浏览器测试得出:
输入 输出 1.0-0.9 == 0.1 False 1.0-0.8 == 0.2 False 1.0-0.7 == 0.3 False 1.0-0.6 == 0.4 True 1.0-0.5 == 0.5 True 1.0-0.4 == 0.6 True 1.0-0.3 == 0.7 True 1.0-0.2 == 0.8 True 1.0-0.1 == 0.9 True
浮点数精度解决方案
解决
如何避免此类非 Bug 型的浮点数计算误差(如 1.0-0.9 != 0.1)?目前常用的一种解决方案是在判断结果前对计算值进行精度缩小处理。由于在缩小精度的过程中会自动执行四舍五入,从而消除误差:
代码如下:
(1.0-0.9).toFixed(digits) // toFixed() 精度参数须在 0 与20 之间
parseFloat((1.0-0.9).toFixed(10)) === 0.1 // 结果为True
parseFloat((1.0-0.8).toFixed(10)) === 0.2 // 结果为True
parseFloat((1.0-0.7).toFixed(10)) === 0.3 // 结果为True
parseFloat((11.0-11.8).toFixed(10)) === -0.8 // 结果为True方法提炼
代码如下:
// 通过isEqual工具方法判断数值是否相等
function isEqual(number1, number2, digits){
digits = digits == undefined? 10: digits; // 默认精度为10
return number1.toFixed(digits) === number2.toFixed(digits);
}
isEqual(1.0-0.7, 0.3); // return true
// 原生扩展方式,更喜欢面向对象的风格
Number.prototype.isEqual = function(number, digits){
digits = digits == undefined? 10: digits; // 默认精度为10
return this.toFixed(digits) === number.toFixed(digits);
}
(1.0-0.7).isEqual(0.3); // return true