Potential problem with Vectors Equals()

I just run into a strange effect where two vectors are not equal, although they “virtually” are.

It has to do with how Floats are processed, and is a normal behaviour to the computer. But is it a behaviour we want for Vectors ?

[java]if (Float.compare(x, comp.x) != 0)[/java]



If you try this :

[java]new Vector2f( -0.0f, 0.0f ).equals( new Vector2f( 0.0f, -0.0f ) )[/java]

It will return false, because 0.0f and -0.0f are different.

I found the origin of the problem…



[java]Math.abs( -0.0f ) returns 0.0[/java]

but

[java]FastMath.abs( -0.0f ) returns -0.0[/java]



There’s an inconsistency in the FastMath library.



[java] public static float abs(float fValue) {

if (fValue < 0) {

return -fValue;

}

return fValue;

}

[/java]



The correct code, as extracted from java.lang.Math would be :

[java] public static float abs(float fValue) {

return (fValue <= 0.0F) ? 0.0F - fValue : fValue;

}

[/java]

With this, FastMath.abs( -0.0f ) returns 0.0

Sorry to bother again,



Can I have an answer about FastMath.abs()? (the post above)

I would like to know if this is a wanted behaviour (faster?). Or should use Math.abs() to solve my vector’s equality problem ? :slight_smile:



On a side note, I added a new method in FastMath to calculate a floorModulo, if you are interested :

[java]

/**

  • Returns the floorModulo, useful with negative numbers
  • @param fValue The value to modulo
  • @param fMod The modulo
  • @return (float)

    */

    public static float floorMod( float fValue, float fMod )

    {

    return ( fValue - ( fMod * (float) Math.floor( fValue / fMod ) ) );

    }

    [/java]

    I use floorModulo to be able to get a localPoint in a TerrainGrid from negative coordinates. :slight_smile:



    Thank you.

It seems that a lot of other classes are using the “Float.compare()” method. We cannot just change it in one place.

The question to ask is, why is Float.compare() used instead of just comparing the values, and should we change it?

my advice would be try to apply the change, if other classes exhibit wrong behavior then they worked on wrong assumption = they too are bugged.

You cannot have software that works with some assumptions and works only for some testcases.

Floating point math is not exact. if you do a calculation and then do this comparison:

[java]if (result == expectedResult)[/java]

then it is unlikely that the comparison will be true. If the comparison is true then it is probably unstable – tiny changes in the input values, compiler, or CPU may change the result and make the comparison be false.

Float.compareTo() compares two Float objects numerically. There are two ways in which comparisons performed by this method differ from those performed by the Java language numerical comparison operators (<, <=, ==, >= >) when applied to primitive float values:

  • Float.NaN is considered by this method to be equal to itself and greater than all other float values (including Float.POSITIVE_INFINITY).
  • 0.0f is considered by this method to be greater than -0.0f.

    This ensures that Float.compareTo(Object) (which forwards its behavior to this method) obeys the general contract for Comparable.compareTo, and that the natural order on Floats is consistent with equals.

    Comparison operators act differently :

    [java](0.0 / 0.0) -> NaN

    2.0 / (0.0) -> Float.POSITIVE_INFINITY

    2.0 / (-0.0) -> Float.NEGATIVE_INFINITY

    (-0.0) == 0.0 -> true

    Float.NaN == 1.0 -> false

    Float.NaN < -3.0 -> false

    Float.NaN > Float.POSITIVE_INFINITY -> false

    Float.NaN < Float.POSITIVE_INFINITY -> false

    Float.POSITIVE_INFINITY == Float.POSITIVE_INFINITY -> true

    -Float.POSITIVE_INFINITY == Float.NEGATIVE_INFINITY -> true

    Float.NEGATIVE_INFINITY < 1.0 -> true

    NaN == NaN -> false[/java]

    Comparing two Java “Float” objects can have different semantics than comparing two Java “float” values. Recall the “float” class is a Java primitive class, while java.lang.Float is a subclass of “Object”.

    A “NaN” value is not equal to itself. However, a “NaN” Java “Float” object is equal to itself. The semantic is defined this way, because otherwise “NaN” Java “Float” objects cannot be retrieved from a hash table.

    In conclusion

    You use Float.compare() to use the Float object to equals() without actually creating two Float instances. But the raw result is wrong with zeroes. A solution should be a dual equality…

    [java]if ( ( x != comp.x ) && (Float.compare(x, comp.x) != 0) return false;[/java]

    With this, (0.0f equal -0.0f) => true ; (NaN != NaN) Float.compares() to 0 => true

This would lead to a new FastMath.fEquals() method.

[java]public boolean fEquals( float a, float b )

{

return ( ( a == b ) || ( Float.compare(a, b) == 0) );

}[/java]