浮点数精度

问题

0.1 + 0.2 === 0.3 // false

0.1 + 0.2 - 0.3 // 5.551115123125783e-17 => e 表示法的实际意义是 5.551115123125783 * 10^-7

原因

关于浮点数值计算会产生舍入误差问题,有一点需要明确:这是使用基于 IEEE754 数值的浮点计算通病,ECMAScript 并非独此一家;其他使用相同数值格式的语言也存在这个问题。

所谓 IEEE754 标准,全称 IEEE 二进制浮点数算术标准,这个标准定义了表示浮点数的格式等内容。

在 IEEE754 中,规定了四种表示浮点数值的方式:单精确度(32位)、双精确度(64位)、延伸单精确度、与延伸双精确度。像 ECMAScript 采用的就是双精确度,也就是说,会用 64 位字节来储存一个浮点数。

解决

如果我们要判断 0.1 + 0.2 和 0.3 之间是否相等,应该怎么解决呢?

方案:设置一个误差范围,通常称为“机器精度”,对 JavaScript 的数字来说,这个值通常是 2^-52(2.220446049250313e-16)

ES6 为我们提供了 Number.EPSILON 这个方法。

Number.EPSILON === Math.pow(2, -52) // true

也可以为 ES6 之前写兼容版本:

if (!Number.EPSILON) {
    Number.EPSILON = Math.pow(2, -52)
}

最后判断两个数值是否相等的写法:

function withinErrorMargin (a, b) {
    return Math.abs(a - b) < Number.EPSILON
}

withinErrorMargin(0.1 + 0.2, 0.3) // true

结语

这篇总结主要参考《JavaScript高级程序设计——Number类型》和冴羽的博客在新窗口打开