views:

115

answers:

4

In JBox2d, there exists the following code for 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;
}

I am wondering what purpose the float<->int bit conversions functions serve, here. Does this provide a way to get around Java's float comparison inaccuracy problem (if such is even possible)? Or is it something else altogether? I am wondering if it is an alternative to the epsilon approach:

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

PS. for the sake of completeness and interest, here is 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;
}

FYI, I can see perfectly why the conversion functions are used in hashCode() -- hash IDs must be integers.

+3  A: 

Double.Nan (Not-a-number) is a special value when it comes to comparison:

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

This prints:

false
true 
gawi
+2  A: 

Floating point representations are not very precise. For example, the number 0.1 cannot be represented precisely. The expression (2.00 - 1.10) == 0.9 yields false in Java. Using floatToIntBits (or doubleToLongBits) will prevent this problem from occurring.

For more information on this issue, read The Floating Point Guide, or the book Effective Java by Josh Bloch.

Also, in Java, there are some differences between float (the primitive) and Float (the object), when you compare them. See the Javadoc for Float.compareTo for more information on this.

Therefore, when comparing floats, always use Float.floatToIntBits, or (even better!) Float.compare or Float.compareTo.

jqno
This is a bad exemple: the comparison yields "true", try it. Probably that the 0.9f literal is exactly the same value as the 2.00f-1.10f result. Also, in the question, we are referring to exact comparisons.
gawi
You're right, I updated it to something that does yield false.
jqno
The type of the expression 2.00 - 1.10 is double, not float. By default, floating-point literals are doubles in Java. If you want floats, you have to add the 'f' suffix.
gawi
@jqno: Thank you. This is exactly what I was looking for when I posted the question -- a way to avoid Java's float comparison inaccuracy. So this is guaranteed, then?
Nick Wiggill
@Nick I would not conclude this at all. It's not for inaccuracy, it's rather for some special values (NaN and +0/-0). The == works as expected using IEEE754 arithmetic.
gawi
@jqno Thanks for clarifying this, my mistake.
Nick Wiggill
+3  A: 

The explanation can be found in Joshua Bloch's Effective Java: float and Float need special treatment because of the existence of -0.0, NaN, positive infinity, and negative infinity. That's why the Sun JVM's Float.equals() looks like this (6u21):

public boolean equals(Object obj)
{
    return (obj instanceof Float)
           && (floatToIntBits(((Float)obj).value) == floatToIntBits(value));
}

So, no, Math.abs() with an epsilon is not a good alternative. From the Javadoc:

If f1 and f2 both represent Float.NaN, then the equals method returns true, even though Float.NaN==Float.NaN has the value false. If f1 represents +0.0f while f2 represents -0.0f, or vice versa, the equal test has the value false, even though 0.0f==-0.0f has the value true.

That's why Eclipse's autogenerated code does that for you.

The Alchemist
In other words, this is completely superior to the epsilon approach? I can't believe my luck, if so.
Nick Wiggill
Well, if you can guarantee that you will not get any `NaNs` or `-0.0` or infinities, then using `Math.abs() < epsilon` might be faster. Maybe.
The Alchemist
I see from comments below that I was mistaking what this does. It solves problems centred around the special float values; it does not deal with float inaccuracy -- as you initially stated in your answer. So ignore my last question.
Nick Wiggill
A: 

I do not know 100%, but most probably they are trying to get around the NaN != NaN problem. If your float happens to be NaN you cannot compare to anything as the result is always false. Comparing the intBits will give you NaN == NaN.

gpeche