Przeczytałem ten post ujemne i dodatnie zero .
W moim rozumieniu następujący kod powinien dać true
i true
jako wynik.
Jednak daje false
i true
jako wynik.
Porównuję zero ujemne z dodatnim zero.
public class Test {
public static void main(String[] args) {
float f = 0;
float f2 = -f;
Float F = new Float(f);
Float F1 = new Float(f2);
System.out.println(F1.equals(F));
int i = 0;
int i2 = -i;
Integer I = new Integer(i);
Integer I1 = new Integer(i2);
System.out.println(I1.equals(I));
}
}
Dlaczego mamy różne zachowanie dla zer dla Integer
i Float
?
i
ii2
są dokładnie takie same. Następnie, gdy tworzysz noweInteger
, oba zawierają dokładnie tę samą wartość.I1.equals(I)
będzie prawdą.int i = Integer.MIN_VALUE, i2 = -i;
…new
tutaj typów opakowań. Wystarczy użyć np.Integer i = 0, i2 = -i; System.out.println(i.equals(i2)); Float f1 = 0f, f2 = -f1; System.out.println(f1.equals(f2));
Odpowiedzi:
Ints i zmiennoprzecinkowe są w Javie zupełnie innymi zwierzętami. Ints są kodowane jako uzupełnienie dwóch , które ma pojedynczą wartość 0. Floats używa IEEE 754 ( 32-bitowy wariant dla float i 64-bit dla double). IEEE 754 jest nieco skomplikowany, ale dla celów tej odpowiedzi wystarczy wiedzieć, że ma on trzy sekcje, z których pierwsza jest bitem znaku. Oznacza to, że dla każdego elementu typu float istnieje wariant dodatni i ujemny¹. Obejmuje to 0, więc liczby zmiennoprzecinkowe faktycznie mają dwie wartości „zero”, +0 i -0.
Nawiasem mówiąc, uzupełnienie tych dwóch, które wykorzystuje ints, nie jest jedynym sposobem kodowania liczb całkowitych w informatyce. Istnieją inne metody, takie jak ich uzupełnianie , ale mają dziwactwa - na przykład mając zarówno +0, jak i -0 jako odrębne wartości. ;-)
Podczas porównywania operacji podstawowych (i dublujących) Java traktuje +0 i -0 jako równe. Ale kiedy je zapakujesz, Java traktuje je osobno, jak opisano w
Float#equals
. Dzięki temu metoda equals jest spójna z ichhashCode
implementacją (a takżecompareTo
), która po prostu wykorzystuje bity liczby zmiennoprzecinkowej (w tym tę podpisaną wartość) i umieszcza je w stanie niezmienionym.Mogli wybrać inną opcję dla equals / hashCode / CompareTo, ale nie zrobili tego. Nie jestem pewien, jakie były rozważania projektowe. Ale przynajmniej w jednym aspekcie
Float#equals
zawsze miał odbiegać od prymitywnych liczb zmiennoprzecinkowych==
: w elementach pierwotnychNaN != NaN
, ale dla wszystkich obiektów,o.equals(o)
musi być również prawdziwa . Oznacza to, że jeśli takFloat f = Float.NaN
, tof.equals(f)
mimo tof.floatValue() != f.floatValue()
.¹ Wartości NaN (nie-liczba) mają bit znaku, ale nie mają innego znaczenia niż dla zamawiania, a Java ignoruje je (nawet dla zamawiania).
źródło
Jest to jeden z wyjątków typu Float
Dlaczego opisano również:
-0 i 0 będą reprezentowane inaczej przy użyciu bitu 31 Float:
Tak nie jest
Integer
źródło
W przypadku liczb całkowitych nie ma rozróżnienia między -0 i 0 dla liczb całkowitych, ponieważ używa reprezentacji komplementu Twos . Więc twój przykład liczb całkowitych
i
ii1
są dokładnie takie same.W przypadku liczb zmiennoprzecinkowych istnieje reprezentacja -0, a jej wartość jest równa 0, ale reprezentacja bitowa jest inna. Dlatego nowy Float (0f) i nowy Float (-0f) miałyby różne reprezentacje.
Możesz zobaczyć różnicę w reprezentacjach bitowych.
A jeśli odejdziesz od
f
deklarowania,-0f
wtedy będzie traktowane jako liczba całkowita i nie zobaczysz żadnej różnicy w wyniku.źródło
0.0f == -0.0f
. Więc inne zachowanie jest tylko wjava.lang.Float
.float
Wskazuję tylko, że w Javie istnieje różnica w zachowaniu między typem pierwotnym , który pod tym względem jest zgodny z IEEE754, ajava.lang.Float
który nie. Tak więc sama różnica w reprezentacji bitowej nie wystarczy, aby to wyjaśnić.