2010-09-08 16 views
18

W JBox2d istnieje następujący kod dla Vec2.equals():Dlaczego warto używać Float.floatToIntBits() w porównaniach float Java?

@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; 
} 

zastanawiam się jakim celu pływak < - służyć> int bitowe funkcje konwersji, tutaj. Czy jest to sposób na poradzenie sobie z problemem niedokładności porównywania błędów float Java (jeśli jest to możliwe)? Czy może jest to coś zupełnie innego? Zastanawiam się, czy jest to alternatywa dla podejścia epsilon:

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

PS. w trosce o kompletność i zainteresowania, tutaj jest 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, widzę doskonale, dlaczego funkcje konwersji są wykorzystywane w hashCode() - Identyfikatory hash muszą być liczbami całkowitymi.

Odpowiedz

21

Wyjaśnienie można znaleźć w Joshua Bloch's Effective Java: float i Float wymagają specjalnego traktowania z powodu istnienia -0.0, NaN, dodatniej nieskończoności i ujemnej nieskończoności. Dlatego wygląda Sun JVM na Float.equals() tak (6u21):

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

Więc nie, Math.abs() z epsilon nie jest dobrą alternatywą. Z Javadoc:

Jeśli f1 i f2 zarówno reprezentować Float.NaN, wówczas metoda equals zwraca true, chociaż Float.NaN == Float.NaN ma wartość false. Jeśli f1 oznacza + 0,0f, podczas gdy f2 oznacza -0,0f, lub odwrotnie, to jednakowy test ma wartość false, nawet jeśli 0.0f == - 0.0f ma wartość true.

To dlatego autogenerowany kod Eclipse robi to za Ciebie.

+0

Innymi słowy, jest to całkowicie lepsze od podejścia epsilon? Nie mogę uwierzyć w moje szczęście, jeśli tak. –

+0

Cóż, jeśli możesz zagwarantować, że nie otrzymasz żadnych 'NaNs' lub' -0.0' lub nieskończoności, użycie 'Math.abs()

+1

Widzę z poniższych komentarzy, że myliłem się, co to robi. Rozwiązuje problemy skoncentrowane wokół specjalnych wartości pływowych; nie dotyczy nieprawidłowości typu "float" - jak początkowo stwierdzono w odpowiedzi. Więc zignoruj ​​moje ostatnie pytanie. –

10

Double.Nan (Not-a-number) jest szczególną wartość, jeśli chodzi o porównania:

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

Drukuje:

false 
true 
2

Nie znam 100%, ale najprawdopodobniej próbują obejść problem NaN! = NaN. Jeśli twój float jest NaN, nie możesz go porównać z niczym, ponieważ wynik zawsze jest fałszywy. Porównanie intBits da Ci NaN == NaN.

Powiązane problemy