MathHelper
浮点近似相等(ULP 比较)
基本信息
| 属性 | 值 |
|---|---|
| 路径 | squeek.tictooltips.helpers.MathHelper |
| 行数 | 25 |
| 方法数 | 2(均 public static boolean) |
| 参考来源 | 源码注释 :10 指向 randomascii 2012 年的浮点比较文章 |
两个重载
| 方法 | 参数 |
|---|---|
equals(float a, float b) (:5) |
委托给下述 4 参版,固定 maxDiff = Float.MIN_NORMAL、maxUlpsDiff = 1 |
equals(float a, float b, float maxDiff, int maxUlpsDiff) (:12) |
完整实现 |
算法(:12-24)
final float absDiff = Math.abs(a - b);
if (absDiff <= maxDiff) return true; // :14 绝对容差先行
final int aSign = (int) Math.signum(a);
final int bSign = (int) Math.signum(b);
if (aSign != bSign) return false; // :18 符号不同直接否
final int aInt = Float.floatToRawIntBits(a);
final int bInt = Float.floatToRawIntBits(b);
int ulpsDiff = Math.abs(aInt - bInt);
return ulpsDiff <= maxUlpsDiff; // :23
三级判定:绝对差容差 → 符号比对 → ULP(Units in the Last Place)位差。
用 floatToRawIntBits 而非 floatToIntBits
floatToRawIntBits 不做 NaN 规范化,
所以 0.0f 与 -0.0f 的位模式不同(0x00000000 vs 0x80000000),
ulpsDiff 会算出 Integer.MIN_VALUE 的绝对值(仍是很大的数)→ 判不等。
但两者符号判定(:16-18)会先返回 false,结论一致。
代价是 NaN 与任意值的 absDiff <= maxDiff 恒为 false,
符号判定 Math.signum(NaN) 返回 NaN,(int) NaN 得 0,
于是 NaN 与 0.0f 会走到 ULP 比较 → 位差很大 → 判不等。行为可接受。
默认容差
单参重载传的是 Float.MIN_NORMAL(最小的正规格化数,约 1.17549435E-38),
不是 0f、也不是 Float.MIN_VALUE(约 1.4E-45)。
所以 equals(a, b) 的实际语义是「差值小到不可能比最小正浮点还小,或相差 1 个 ULP」,
实际等价于纯 ULP 相等判定。
调用点
grep 全仓:本类在 mod 源码内无任何调用点
(equals(float, float) 与 4 参版都未被引用)。
与 RomanNumeralHelper 的 fromRoman 同属未使用的对称 API。
apiPackage 在 gradle.properties 中为空,不对外暴露,
所以这两个方法实际上是死代码。
潜在误用风险
若将来有人拿它比 handleDurability / flightSpeedMax 这类
量级差异很大的字段,ULP 判定会过于严格(要求几乎逐位相同),
而绝对容差 Float.MIN_NORMAL 对这些值形同虚设。
真正的宽松比较需要传显式的 maxDiff。