【JS篇】为什么 0.1 + 0.2 !== 0.3,以及如何让它们相等?

163 阅读2分钟

在 JavaScript 开发中,我们经常会遇到这样的问题:

console.log(0.1 + 0.2); // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3); // false

这与我们在数学上的直觉不符。为什么会这样?又该如何解决呢?

本文将从底层原理、IEEE 754 标准出发,系统讲解浮点数精度丢失的原因,并提供实际开发中判断浮点数是否相等的解决方案


一、根本原因:浮点数在计算机中的表示方式

JavaScript 中所有数字(包括整数和小数)都使用 64 位双精度浮点数(Double-precision floating-point) 表示,遵循 IEEE 754 标准。

✅ IEEE 754 双精度格式结构如下:

部分占用位数描述
符号位(Sign)1 位表示正负数
指数位(Exponent)11 位表示指数部分
小数位(Fraction / Mantissa)52 位表示有效数字

由于二进制无法精确表示某些十进制小数(如 0.1 和 0.2),只能以近似值存储,这就导致了精度丢失


二、具体分析:0.1 和 0.2 的二进制表示

🔹 0.1 的二进制:

0.00011001100110011...(无限循环)

🔹 0.2 的二进制:

0.0011001100110011...(无限循环)

这两个数在转换为二进制后都是无限循环小数,而 IEEE 754 规定最多只保留 52 位的小数部分,因此必须进行舍入处理。

最终,在计算机内部保存的实际上是这两个数的近似值,所以加起来的结果也不是精确的 0.3。


三、结果验证:0.1 + 0.2 等于多少?

console.log(0.1 + 0.2); // 0.30000000000000004

这个结果就是两个近似值相加后的误差累积结果。


四、解决方案:如何让浮点数“相等”?

✅ 方法一:使用 toFixed() 四舍五入比较

parseFloat((0.1 + 0.2).toFixed(1)) === 0.3; // true

📌 说明:

  • toFixed(n) 返回一个字符串,保留 n 位小数;
  • 再使用 parseFloat() 转回数字;
  • 注意:这种方式会丢失精度控制,不推荐用于金融计算;

✅ 方法二:使用 Number.EPSILON 判断误差范围(推荐)

ES6 引入了 Number.EPSILON,它是 JavaScript 中最小的可表示差值(约为 2^-52),可以用来判断两个浮点数是否足够接近。

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

console.log(isEqual(0.1 + 0.2, 0.3)); // true

📌 优点:

  • 更科学、更严谨;
  • 不依赖四舍五入;
  • 适用于高精度场景;

五、一句话总结

JavaScript 使用 IEEE 754 双精度浮点数来表示数字,不能准确表示像 0.10.2 这样的十进制小数,从而导致 0.1 + 0.2 !== 0.3。要让它们“相等”,应使用 Number.EPSILON 判断误差范围或通过 toFixed() 控制精度。


💡 进阶建议

  • 在金融计算、货币运算中使用 decimal.jsbig.js 等库避免精度问题;
  • 学习 IEEE 754 浮点数规范,深入理解浮点数的存储机制;
  • 在 Vue / React 等框架中对数据做格式化展示时,结合 toFixed()Number.EPSILON 提升用户体验;
  • 使用 TypeScript 的类型系统提前发现潜在的数值错误;