根本原因在于IEEE 754标准下十进制小数(如0.1、0.2)在二进制中为无限循环小数,存储时被截断产生固有舍入误差,而==是逐位精确比对二进制表示,故0.1f+0.2f≠0.3f,且所有遵循该标准的语言均存在此现象。

Java 中浮点数(float 和 double)直接用 == 比较有风险,根本原因在于:它们在内存中以二进制形式按 IEEE 754 标准存储,而多数常用十进制小数(如 0.1、0.2、0.3)无法被精确表示,计算过程必然引入微小舍入误差。
为什么 == 判断会失真
看似相等的两个值,其二进制位模式可能不同,== 是逐位比对,只要有一个比特不一致就返回 false:
- 0.1 + 0.2 实际结果是 0.30000000000000004,而字面量 0.3 存的是另一个近似值,两者 !=
- 从 JSON 解析、数据库读取、类型转换(如
double → float)或序列化反序列化后的值,哪怕语义相同,内存值也可能不同 - 在
if、while等流程控制中,一次误判就可能导致分支跳过、循环卡死或结算漏单
推荐的安全比较方式
放弃“完全相等”的执念,改用“业务可接受的近似相等”:
- 用
Math.abs(a - b) <= epsilon替代a == b,其中 epsilon 是容差阈值 - 金额类场景建议 1e-6(对应分精度),比例/权重类可用 1e-5 或 1e-4
- 避免使用
Double.compare(a, b) == 0——它只判断位模式是否一致,不支持容差,且会把-0.0 == 0.0判为不等
更彻底的规避策略
对精度敏感的业务,从源头减少浮点数参与逻辑判断:
立即学习“Java免费学习笔记(深入)”;
- 金额统一存为 整数(单位:分),所有运算基于
long进行,展示时再转成元 - 配置类数值(如折扣率 5%)存为 整数 5,判断写成
if (discountPercent == 5) - 必须用高精度计算时,选用
BigDecimal,但构造必须用字符串:new BigDecimal("0.1"),禁用new BigDecimal(0.1)
配套保障措施
单靠人工改代码容易遗漏,需建立长效机制:
- 单元测试中显式构造含误差的输入(如
0.1 + 0.2),验证分支是否走对 - 静态扫描工具对项目中所有
float ==、double ==出现位置自动告警 - 线上关键结算路径增加日志,记录原始值、计算后值、
abs(a-b)结果及是否落入 epsilon 范围


















