应使用容差值 epsilon 判断 double 相等,因浮点数二进制无法精确表示十进制小数(如 0.1+0.2≠0.3),== 比较会因精度误差返回 false,Double.compare 也无法解决精度问题。

Java里直接用 == 比较两个 double 会出错
浮点数在二进制中无法精确表示大多数十进制小数,比如 0.1 + 0.2 的结果不是 0.3,而是 0.30000000000000004。所以用 == 判断相等大概率失败,哪怕肉眼看起来一样。
常见错误现象:0.1 + 0.2 == 0.3 返回 false;单元测试里断言 assertEquals(expected, actual) 报错但数值打印出来“明明一样”。
- 永远别对
float或double用==做逻辑相等判断 - 连
Double.compare(a, b) == 0都不行——它比较的是 IEEE 754 位模式,仍会把0.0和-0.0当作不同,且不解决精度误差问题 - 需要自己定义“多近才算相等”,也就是引入一个极小的容差值
epsilon
用 Math.abs(a - b) 是最常用做法
这是手动实现浮点数“近似相等”的核心逻辑:计算两数之差的绝对值,看是否落在可接受误差范围内。
使用场景:写工具方法、单元测试断言、业务逻辑中需判断计算结果是否落在预期区间(如物理模拟、金融四舍五入前校验)。
立即学习“Java免费学习笔记(深入)”;
-
epsilon不是越小越好——设成1e-17可能比double的最低有效位还小,反而没意义 - 典型取值:
1e-6(适合一般工程计算),1e-9(高精度需求),1e-15(接近double机器精度极限) - 注意:如果
a或b是极大值(如1e308),相对误差更合理,此时应改用Math.abs(a - b)
简单示例:
public static boolean equals(double a, double b, double epsilon) {
return Math.abs(a - b) < epsilon;
}JUnit 5 的 Assertions.assertEquals(double, double, double) 就是封装了这个逻辑
写测试时别自己手写容差判断,直接用框架提供的重载方法,语义清晰且自带报错信息。
参数差异:assertEquals(expected, actual, epsilon) —— 第三个参数是最大允许误差,不是百分比,也不是相对误差。
- 如果漏传第三个参数,调用的是
assertEquals(double, double),它内部用的是Double.compare,**仍然会出错** - JUnit 4 用的是
assertEquals(double, double, double),签名一致,但底层实现略有不同(不影响使用) - 注意:这个方法对
NaN的处理是——只要任一参数为NaN,就返回false(符合 IEEE 754 规范)
别忽略 Float 和 Double 的静态方法 isFinite 和 isNaN
真实业务中,参与比较的浮点数可能来自外部输入、解析失败或计算溢出,出现 NaN、Infinity 或 -Infinity。这时候直接套用 Math.abs(a - b) 会得到意外结果。
例如:Math.abs(Double.NaN - 1.0) 是 NaN,而 NaN 永远为 <code>false,但你可能希望它抛异常或明确返回 false。
- 建议在比较前加校验:
if (!Double.isFinite(a) || !Double.isFinite(b)) return false; -
Double.isNaN(x)比x != x更可读,也更安全(后者在某些 JIT 优化下可能被误判) - 如果业务允许
Infinity == Infinity,需单独判断:Double.isInfinite(a) && Double.isInfinite(b) && Math.signum(a) == Math.signum(b)
精度阈值本身不是魔法数字,它得和你的数据量级、计算路径长度、硬件平台都挂钩。同一个 epsilon 在单次加法和迭代 1000 次的累加器里,意义完全不同。









