做参数一致性检查的时候遇到个怪事:明明几个文件的相关参数是一样的,检查却报”不一致”。打印出来一看,一个是 8.0,另一个是 7.9999995。

原因是这些参数从文件头里按 float(单精度)读出来,再赋给 double 做比较。float 的有效位大概只有 7 位十进制,一个理论上等于 8 的值,存进 float 再转 double,很可能就变成 7.9999995… 这种近似值。原来的检查直接用 != 精确比较,把这种近似相等的值判成了不等。

// 原来:精确比较,float 的近似噪声会被误判
if (p.timeWindow != first.timeWindow) { /* 报不一致 */ }

// 改成容差比较
private fun approxEqual(a: Double, b: Double): Boolean =
abs(a - b) < 1e-3

if (!approxEqual(p.timeWindow, first.timeWindow)) { /* 报不一致 */ }

浮点数不能直接用 == / != 比,根子在存储方式:浮点是二进制小数加指数,存不下的就舍入。像 0.1 这种十进制小数,在二进制里是无限循环的,存进 float/double 就是个近似值。两个”应该相等”的浮点,经历了不同的运算路径,舍入误差累积不同,末位就对不上,== 判出来就是 false。

正确比法是看差值的绝对值是不是在一个容差范围内:

abs(a - b) < epsilon

容差选多大看业务。这个场景里,真实的参数差异都差着好几个量级(比如 8 和 9、30 和 60),容差给 1e-3 绰绰有余,既滤掉了 float 的近似噪声,又不会把真正的差异判成相同。如果业务上本身就要区分 0.001 级别的差异,容差得相应调小,或者换 Math.ulp、相对误差的比法。

顺带一提,整数不用这么折腾。整数类型的参数还是直接 != 精确比,因为整数不存在浮点那种舍入噪声。所以同一个检查里,浮点参数走容差、整数参数走精确相等,各用各的。