浮点数在集合查找中做等值判断会失真,因其二进制表示存在舍入误差,导致哈希、排序、去重等操作失败;应改用容差比较、标准化或整数/bigdecimal替代。

在集合查找中对浮点数做等值判断(比如用 == 或 equals())会失真,不是因为代码写错了,而是浮点数本身在计算机里就存不准。
根本原因:二进制无法精确表达多数十进制小数
像 0.1、0.01、0.3 这些常见小数,在二进制下是无限循环小数(类似十进制里的 1/3 = 0.333…),而 IEEE 754 标准只能用有限位(float 23 位尾数,double 52 位)去截断存储。结果就是:
- 0.1f + 0.2f 实际存的是 0.30000001192092896
- 字面量 0.3f 存的也是近似值,但可能和上面那个“看起来一样、内存不同”
集合操作放大微小差异
查找常依赖哈希、排序或逐项比对,而这些都对“比特级一致”敏感:
- 用浮点数作 key 放进
HashSet或ConcurrentHashMap,两个逻辑相等的值因舍入路径不同,哈希码不同 → 被当成两个 key - 按浮点字段排序后二分查找,误差导致目标值插在错误位置 → 查不到
- 集合去重时逐元素比较,
a[i] == b[i]对其中一个元素失败 → 整组判为不等
常见失真场景
这些不是边缘情况,而是真实高频问题:
- 文件元数据匹配:按
size == 1024.0查找,实际文件大小是1024.0000000000002→ 直接跳过 - 哈希分片路由:用
score % N分发任务,score 是计算得来,微小误差让取模结果从 2 变成 3 → 文件被送到错误线程处理 - 条件过滤失效:
if (confidence == 0.95),但模型输出或 JSON 解析后变成0.9499999999999999→ 整条识别结果被丢弃
可靠替代方案
不追求“完全相等”,转而保障“业务上可接受的一致”:
- 改用容差比较:遍历或判等时,用
Math.abs(a - b) (float)或 <code>(double) - 提前标准化:读入后立即四舍五入到业务所需精度,如
Math.round(x * 100.0) / 100.0(保留两位小数) - 绕开浮点数:金额存“分”(整数)、坐标转字符串索引、关键判等用
BigDecimal(务必用字符串构造,避免 new BigDecimal(0.1))











