+0 e -0 mostrano un comportamento diverso per i dati int e float


16

Ho letto questo post zero positivo e negativo .

Per la mia comprensione il seguente codice dovrebbe dare true e true come output.

Tuttavia, sta dando falsee truecome output.

Sto confrontando lo zero negativo con uno zero positivo.

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

Perché abbiamo un comportamento diverso per 0 per Integere Float?


11
Se si controlla javadocs , docs.oracle.com/javase/8/docs/api/java/lang/… La definizione consente il corretto funzionamento delle tabelle hash. Inoltre, non esiste un numero intero -0.
matt

@matt se -0 non è un numero intero allora dovrebbe essere valutato come falso ...
Joker l'

3
Quando dici i2 = -i; i2 prende l'esatta rappresentazione in bit di i, non c'è modo di discernerli. ie i2sono esattamente gli stessi. Quindi, quando crei nuovi messaggi Integer, entrambi racchiudono lo stesso valore esatto. I1.equals(I)sarà vero.
matt

1
Prova int i = Integer.MIN_VALUE, i2 = -i;...
Holger l'

1
A proposito, non c'è motivo di utilizzare newper i tipi di wrapper qui. Basta usare, ad esempioInteger i = 0, i2 = -i; System.out.println(i.equals(i2)); Float f1 = 0f, f2 = -f1; System.out.println(f1.equals(f2));
Holger l'

Risposte:


19

Ints e float sono bestie abbastanza diverse in Java. Gli ints sono codificati come complemento a due , che ha un singolo valore 0. I float usano IEEE 754 (la variante a 32 bit per i float e 64 bit per i doppi). IEEE 754 è alquanto complesso, ma ai fini di questa risposta, devi solo sapere che ha tre sezioni, la prima delle quali è un bit di segno. Ciò significa che per qualsiasi galleggiante, c'è una variante positiva e negativa¹. Ciò include 0, quindi i float hanno in realtà due valori "zero", +0 e -0.

A parte questo, il complemento a due che utilizza non è l'unico modo per codificare numeri interi in informatica. Esistono altri metodi, come il complemento di quelli , ma hanno delle stranezze - come avere sia +0 che -0 come valori distinti. ;-)

Quando si confrontano le primitive float (e le doppie), Java considera +0 e -0 come uguali. Ma quando li inscatoli, Java li tratta separatamente, come descritto in Float#equals. Ciò consente al metodo equals di essere coerente con la loro hashCodeimplementazione (oltre che compareTo), che utilizza solo i bit del float (incluso quel valore con segno) e li inserisce così com'è in un int.

Avrebbero potuto scegliere qualche altra opzione per equals / hashCode / compareTo, ma non lo fecero. Non sono sicuro di quali fossero le considerazioni sul design. Ma almeno in un aspetto, Float#equalssarebbe sempre stato diverso dai primitivi float ==: nei primitivi NaN != NaN, ma per tutti gli oggetti, o.equals(o)deve anche essere vero . Ciò significa che se l'hai avuto Float f = Float.NaN, allora f.equals(f)anche se f.floatValue() != f.floatValue().


¹ I valori NaN (non un numero) hanno un bit di segno, ma non hanno alcun significato se non per l'ordinamento e Java lo ignora (anche per l'ordinamento).


10

Questa è una delle opzioni Float equivale a un'eccezione

ci sono due eccezioni:

Se f1 rappresenta + 0,0f mentre f2 rappresenta -0,0f , o viceversa, il test uguale ha il valore falso

Il perché è anche descritto:

Questa definizione consente alle tabelle hash di funzionare correttamente.

-0 e 0 saranno rappresentati diversamente usando il bit 31 di Float:

Il bit 31 (il bit selezionato dalla maschera 0x80000000) rappresenta il segno del numero in virgola mobile.

Questo non è il caso Integer


domanda è perché? È questa regola dura e veloce che dobbiamo stipare :(
Joker l'

@Joker Aggiunta la citazione che consente alle tabelle hash di funzionare correttamente
user7294900

4
Un pezzo chiave che questa risposta (e il javadoc) non menzionano è che la differenza è che nei float, +0 e -0 sono valori diversi - equivalenti, ma diversi. Fondamentalmente, i float hanno tre parti e la prima parte è un singolo bit che indica se il float è positivo o negativo. Questo non è il caso degli ints (come rappresentato in Java), che hanno solo un singolo valore 0.
yshavit,

@yshavit Grazie, potresti condividere la stessa risposta
Joker l'

3
@Joker Il bit 31 (il bit selezionato dalla maschera 0x80000000) rappresenta il segno del numero in virgola mobile.
user7294900

5

Per gli interi, non esiste alcuna distinzione tra -0 e 0 per gli interi perché utilizza la rappresentazione del complimento Twos . Quindi il tuo esempio intero ie i1sono esattamente gli stessi.

Per i float esiste una rappresentazione -0 e il suo valore è equivalente a 0, ma la rappresentazione dei bit è diversa. Quindi il nuovo Float (0f) e il nuovo Float (-0f) avrebbero rappresentazioni diverse.

Puoi vedere la differenza nelle rappresentazioni dei bit.

System.out.println(Float.floatToIntBits(-0f) + ", " + Float.floatToIntBits(0f));

-2147483648, 0

E se si interrompe il fper dichiarare il, -0fallora verrà trattato come un numero intero e non si noterà alcuna differenza nell'output.


Eppure il float primitivo sembra funzionare bene con quello. Questo è 0.0f == -0.0f. Quindi il diverso comportamento è solo in java.lang.Float.
Ivant

3
@ivant secondo IEEE754, "Le normali operazioni di confronto, tuttavia, trattano le NaN come non ordinate e confrontano −0 e +0 come uguali" en.m.wikipedia.org/wiki/IEEE_754
Andy Turner

@AndyTurner, sì, lo capisco. Sto solo sottolineando che in Java c'è una differenza nel comportamento tra il tipo primitivo float, che è conforme a IEEE754 in questo senso e java.lang.Floatche non lo è. Quindi solo la differenza nella rappresentazione dei bit non è sufficiente per spiegare questo.
Ivant
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.