<p>应使用容差比较而非 == 判断 double 相等,因二进制浮点数无法精确表示多数十进制小数,计算中会产生舍入误差;需根据数值量级选择合适 epsilon,用 Math.abs(a - b) < epsilon 判断。</p>

直接用 == 比较两个 double 会出错
Java 中 double 和 float 是二进制浮点数,无法精确表示大多数十进制小数(比如 0.1),计算过程还会累积舍入误差。所以 a == b 在绝大多数浮点运算后都会返回 false,哪怕数学上它们“应该相等”。
常见错误现象:
– 0.1 + 0.2 == 0.3 返回 false
– 循环累加 0.1 十次后与 1.0 比较失败
– 单元测试里断言 assertEquals(expected, actual) 报错,但数值看起来一样
- 永远不要对
double/float使用==判断逻辑相等 - 必须引入一个极小的容差值(epsilon),判断差值是否落在这个范围内
- epsilon 不是固定写死
1e-6就万事大吉——它得和你要比较的数值量级匹配
Math.abs(a - b) 是最简方案,但有陷阱
这是最常写的写法,但容易忽略边界情况。比如当 a 或 b 是无穷大、NaN,或者两者都极大时,a - b 可能溢出或失去精度。
- 先检查
Double.isNaN(a) || Double.isNaN(b),NaN与任何数(包括自己)都不相等 - 如果
a和b同为Double.POSITIVE_INFINITY或同为Double.NEGATIVE_INFINITY,可直接认为相等 - 普通场景下用
Math.abs(a - b) 没问题,但 epsilon 要选对:比较 <code>1e-10级别的数,用1e-6就太大;比较1e9级别的数,用1e-6又太小 - 推荐用相对误差:比如
Math.abs(a - b) ,但要注意分母为 0 的情况
用 Double.compare() 或 Double.equals()?别被名字骗了
这两个方法不是为“近似相等”设计的:Double.compare(a, b) 比较的是 IEEE 754 规范下的精确序关系;Double.equals() 内部调用的是 doubleToLongBits() 的位比较,连 -0.0 == 0.0 都返回 false(因为符号位不同)。
立即学习“Java免费学习笔记(深入)”;
-
Double.equals(a, b)对0.0和-0.0返回false,而数学上它们相等 - 它不处理
NaN:两个NaN的Double.equals()返回false(符合规范,但不符合业务直觉) - 它完全无视误差——和
==一样脆弱 - 除非你明确要比较 bit pattern(比如序列化校验),否则别用它们做“值相等”判断
JUnit 5 的 Assertions.assertEquals(double expected, double actual, double epsilon)
这是最贴近实际开发的用法。单元测试里几乎不需要手写误差逻辑,JUnit 已封装好健壮实现。
- 第三个参数就是 epsilon,必须显式传入,没有默认值
- 它内部做了
NaN和无穷大的安全判断,比手写更可靠 - 注意:这个重载只存在于
org.junit.jupiter.api.Assertions,老版 JUnit 4 的Assert.assertEquals(double, double, double)行为类似,但 JUnit 5 更推荐用新 API - 示例:
Assertions.assertEquals(0.3, 0.1 + 0.2, 1e-10);—— 这行能过,而不用epsilon的版本必挂
真正难的不是写那行 Math.abs(a - b) ,而是想清楚 eps 该取多大、要不要考虑数量级、要不要兼容 <code>NaN 和无穷大。这些细节一旦漏掉,bug 就藏在看似正常的计算结果里。

















