2014-05-02 12 views
9

Piszę klasę Vec2D, reprezentującą dwuwymiarowy wektor. Przechowuję x i y w double s.Znaczenie Double.doubleToLongBits (x)

Pytany wygenerować equals(Object obj i hashCode() Eclipse generowane w ten sposób:

@Override 
public int hashCode() { 
    final int prime = 31; 
    int result = 1; 
    long temp; 
    temp = Double.doubleToLongBits(x); 
    result = prime * result + (int) (temp^(temp >>> 32)); 
    temp = Double.doubleToLongBits(y); 
    result = prime * result + (int) (temp^(temp >>> 32)); 
    return result; 
} 
@Override 
public boolean equals(Object obj) { 
    if (this == obj) 
     return true; 
    if (obj == null) 
     return false; 
    if (getClass() != obj.getClass()) 
     return false; 
    Vec2D other = (Vec2D) obj; 
    if (Double.doubleToLongBits(x) != Double.doubleToLongBits(other.x)) 
     return false; 
    if (Double.doubleToLongBits(y) != Double.doubleToLongBits(other.y)) 
     return false; 
    return true; 
} 

Jakie jest znaczenie Double.doubleToLongBits(x) w tym kontekście? Czy nie mogę po prostu napisać: x != other.x?

Odpowiedz

9

Krótka odpowiedź: Eclipse wykorzystuje Double.doubleToLongBits bo to właśnie Double.equals robi:

Rezultatem jest true wtedy i tylko wtedy, gdy argument nie jest null i to dwukrotnie obiekt, który reprezentuje doubl E, który ma taka sama wartość jak double reprezentowana przez ten obiekt. W tym celu dwie wartości double są uważane za takie same wtedy i tylko wtedy, gdy metoda doubleToLongBits(double) zwraca identyczną wartość long po zastosowaniu do każdego z nich.

Długa odpowiedź: JLS określa kilka różnic między Double.equals i ==. Dla jednej różnicy określonej w JLS 4.2.3 i JLS 15.21.1:

Pozytywne i negatywne zerowy zerowy porównania równe; tak więc wynik wyrażenia 0.0==-0.0 jest wynikiem tego, że wynik jest następujący: .Ale inne operacje mogą odróżnić pozytywne i negatywne zero; na przykład 1.0/0.0 ma wartość dodatnią nieskończoności, a wartość 1.0/-0.0 jest ujemną nieskończonością.

Kolejne pozdrowienia NaN:

Jeśli któryś argument jest NaN, wówczas wynikiem == jest false ale wynik != jest true.

Rzeczywiście, test x!=x jest wtedy i tylko wtedy, gdy wartość x jest NaN.

Jak widać, jest to możliwe dla dwóch wartości podwójnych porównać z == ale faktycznie odpowiadają różne zachowanie kiedy używane w matematyce i hash tabel. Tak więc, pisząc wygenerowaną metodę równości, Eclipse przyjmuje założenie, że dwa podwojenia są równe tylko wtedy i tylko wtedy, gdy wszystkie operacje, które można z nimi zrobić, są identyczne, lub (równoważnie), jeśli były one autoboxowane i porównywane z ich metodami equals. Jest to szczególnie ważne, jeśli przełączanie między double i Double -jest szczególnie nieoczekiwane, ponieważ właściwości równości są tam różne.

Oczywiście możesz odejść od tego założenia: Niezależnie od tego, czy jest to dobry pomysł, możesz przypisać specjalne przypadki do dowolnej z wielu możliwych reprezentacji NaN, w takim przypadku Double.doubleToRawLongBits() będzie lepiej pasować do Twojego Metody: equals i hashCode. Z tego samego powodu, twój przypadek użycia może traktować obiekty z +0,0 i -0,0 jako równoważne i gwarantować, że wartości NaN nie są możliwe, w takim przypadku surowe porównanie == może działać lepiej dla equals (ale w którym momencie emulacja tych samych kryteriów dla hashCode staje się trudne).

0

Szybki rzut oka na rentowności javadoc internetowych następująco:

Zwraca reprezentację podanej wartości zmiennoprzecinkowej zgodnie z IEEE 754 zmiennoprzecinkowych „podwójnym formacie” układ bitów.

...

We wszystkich przypadkach wynik jest długi liczba całkowita, w przypadku podawania do longBitsToDouble Sposób (długości), wytworzy wartość zmiennoprzecinkową taki sam jak argumentu doubleToLongBits (oprócz wszystkich Wartości NaN są zawijane do pojedynczej "kanonicznej" wartości NaN).

Więc to chyba sposobem standaryzacji double reprezentacje x i y, jak NaN może mieć wiele double reprezentacje

3

Ponieważ == i != następują IEEE-754 dla dwuosobowych, semantyki i 0.0 == -0.0Double.NaN != Double.NaN. Te zachowania mogą nie być tym, czego potrzebujesz, więc Double.doubleToLongBits() konwertuje 64 bity danych double na 64 bity danych long, tak że działają operacje takie jak przesunięcia bitów i XOR.

Szczerze, choć powiedziałbym, że stosowanie doubleToLongBits tu jest błąd, ponieważ jeśli dbasz o dokładnym równości należy używać Double.doubleToRawLongBits() (który nie wykonuje żadnych tłumaczeń na danych double w ogóle) zamiast.