为什么在Java中的浮点数比较中要使用Float.floatToIntBits()?

20
在JBox2d中,存在以下用于Vec2.equals()的代码:
@Override
public boolean equals(Object obj) { //automatically generated by Eclipse
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    Vec2 other = (Vec2) obj;
    if (Float.floatToIntBits(x) != Float.floatToIntBits(other.x))
        return false;
    if (Float.floatToIntBits(y) != Float.floatToIntBits(other.y))
        return false;
    return true;
}

我想知道这里的float<->int位转换函数有什么用途。这是否提供了一种解决Java中float比较不准确问题的方法(如果可能的话)?还是完全不同的东西?我想知道它是否是epsilon方法的替代方案:

if (Math.abs(floatVal1 - floatVal2) < epsilon)

顺便提一下,为了完整性和趣味性,这里是Vec2.hashCode()的实现:

@Override
public int hashCode() { //automatically generated by Eclipse
    final int prime = 31;
    int result = 1;
    result = prime * result + Float.floatToIntBits(x);
    result = prime * result + Float.floatToIntBits(y);
    return result;
}

顺便提一下,我完全能理解为什么在hashCode()中使用转换函数——哈希ID必须是整数。

3个回答

23
解释可以在Joshua Bloch的《Effective Java》中找到:由于存在-0.0NaN、正无穷和负无穷,所以floatFloat需要特殊处理。这就是为什么Sun JVM的Float.equals()看起来像这样(6u21):
public boolean equals(Object obj)
{
    return (obj instanceof Float)
           && (floatToIntBits(((Float)obj).value) == floatToIntBits(value));
}
所以说,Math.abs()与一个极小值并不是很好的替代方案。参考 Javadoc 文档:
如果 f1 和 f2 都表示 Float.NaN,那么 equals 方法返回 true,即使 Float.NaN == Float.NaN 的结果为 false。如果 f1 表示 +0.0f,而 f2 表示 -0.0f,或者反过来,则相等测试结果为 false,即使 0.0f == -0.0f 的结果为 true。
这就是为什么 Eclipse 自动生成的代码会为你处理这个问题。

换句话说,这完全比ε方法更优?如果是这样的话,我简直不敢相信自己的运气。 - Engineer
如果您能保证不会出现任何"NaNs"或"-0.0"或无穷大,那么使用"Math.abs() < epsilon"可能会更快。也许。 - The Alchemist
1
我从下面的评论中看到我误解了这个程序的作用。它解决的是围绕特殊浮点值的问题;它并不处理浮点数的不准确性——正如你最初在回答中所述。因此,请忽略我的最后一个问题。 - Engineer
Math.abs() < epsilon 可能更快,但 epsilon 应该随着您的值的变化而改变。在这里查看解释:http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ 因此,除非您实现自己的函数来计算一个好的 epsilon,否则您可能会使用 equals(),因为它总是会给出正确的答案(根据您系统的可用精度,并且除了 -0.0f 到 0.0f 的情况)。 - dberm22

10

Double.Nan(非数字)在比较方面是一个特殊的值:

System.out.println(Float.NaN == Float.NaN);
System.out.println(Float.floatToIntBits(Float.NaN) == Float.floatToIntBits(Float.NaN));

这将打印:

false
true 

3

我不是百分之百确定,但很可能他们试图绕过NaN!= NaN的问题。如果你的浮点数恰好是NaN,则无法与任何东西进行比较,因为结果始终为false。比较intBits将使您获得NaN == NaN。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接