浮点数比较不可靠是因为IEEE 754标准下二进制无法精确表示十进制小数(如0.1),导致0.1+0.2≠0.3,必须用epsilon容差判断“足够接近”,而非数学相等。

因为浮点数在计算机中是近似存储的,直接用 == 比较 float 或 double 几乎总是不可靠的。哪怕两个数在数学上完全相等,比如 0.1 + 0.2 和 0.3,它们在内存里实际存储的二进制值也会有微小差异——这是 IEEE 754 标准下二进制无法精确表示十进制小数导致的舍入误差。
浮点数的“相等”本质是“足够接近”
float 和 double 都按 IEEE 754 规则编码:单精度(32 位)约 6–9 位有效数字,双精度(64 位)约 15–17 位。连续运算会累积误差,所以“相等”只能理解为“差值落在可接受范围内”。这个范围就是 epsilon。
- 它不是数学意义上的相等判断,而是工程意义上的容差判定
- 不设 epsilon 就等于要求两个浮点数的每一位二进制位都完全一致——这在绝大多数计算场景中既不可能,也不合理
- Java 中
Float.equals()和Double.equals()仍做位比较,对 NaN 行为特殊,不能替代 epsilon 判断
epsilon 不是固定常量,得看数据范围和精度需求
用错 epsilon 值比不用更危险:太大会把明显不同的数判为相等;太小又无法覆盖正常误差。
-
绝对误差法:
Math.abs(a - b) < epsilon——适合值域集中、数量级稳定的情况,比如归一化坐标(0~1)、插值系数、角度余弦值 -
相对误差法:
Math.abs(a - b) < epsilon * Math.max(Math.abs(a), Math.abs(b))——适合跨数量级比较,如物理模拟中从毫米到光年的距离 -
混合误差法:
Math.abs(a - b) < epsilon * (1 + Math.max(Math.abs(a), Math.abs(b)))——兼顾小值防除零、大值防失效,通用性最强
不同场景推荐的 epsilon 数量级
选值要匹配类型和业务敏感度:
-
float(32 位):常用
1e-5f~1e-6f,图形渲染、嵌入式传感数据够用 -
double(64 位):科学计算常用
1e-9,金融类逻辑建议1e-12或改用BigDecimal -
注意字面量类型:写
float f = 3.14f;,末尾加f;写double d = 3.14;才合法,否则编译报错“possible loss of precision”
还要避开几个隐形坑
epsilon 比较不是万能膏药,边界情况必须单独处理:
-
NaN:任何与 NaN 的比较(包括
==、Math.abs(NaN))都返回 false,需先用Float.isNaN()或Double.isNaN()检查 - 无穷大:±Infinity 之间的差值是 NaN,同样会导致比较逻辑失效
-
业务关键值:比如金额、库存数量、状态开关,应避免浮点参与判断,优先转整型或用
BigDecimal精确运算

















