+0 et -0 montre un comportement différent pour les données int et float

16

J'ai lu ce post zéro négatif et positif .

À ma compréhension, le code suivant devrait donner true et true en sortie.

Cependant, il donne falseet truecomme sortie.

Je compare un zéro négatif avec un zéro positif.

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

Pourquoi avons-nous un comportement différent pour les 0 pour Integeret Float?

Joker
la source
11
Si vous vérifiez les javadocs, docs.oracle.com/javase/8/docs/api/java/lang/… La définition permet aux tables de hachage de fonctionner correctement. De plus, il n'y a pas de -0 entier.
mat
@matt si -0 n'est pas un entier, il doit être évalué comme faux ...
Joker
3
Lorsque vous dites i2 = -i; i2 prend la représentation binaire exacte de i, il n'y a aucun moyen de les discerner. iet i2sont exactement les mêmes. Ensuite, lorsque vous créez de nouveaux Integers, ils enveloppent tous les deux la même valeur exacte. I1.equals(I)sera vrai.
mat
1
Essayez int i = Integer.MIN_VALUE, i2 = -i;
Holger
1
Il n'y a d'ailleurs aucune raison d'utiliser newles types d'encapsuleurs ici. Il suffit d'utiliser, par exempleInteger i = 0, i2 = -i; System.out.println(i.equals(i2)); Float f1 = 0f, f2 = -f1; System.out.println(f1.equals(f2));
Holger

Réponses:

19

Ints et flotteurs sont des bêtes assez différentes en Java. Les entiers sont codés comme un complément à deux , qui a une seule valeur 0. Les flotteurs utilisent IEEE 754 (la variante 32 bits pour les flottants et 64 bits pour les doubles). IEEE 754 est quelque peu complexe, mais pour les besoins de cette réponse, il vous suffit de savoir qu'il comporte trois sections, dont la première est un bit de signe. Cela signifie que pour tout flotteur, il existe une variante positive et négative¹. Cela inclut 0, donc les flottants ont en fait deux valeurs "zéro", +0 et -0.

Soit dit en passant, le complément à deux que les ints utilisent n'est pas le seul moyen de coder des entiers en informatique. Il existe d'autres méthodes, comme celles de complément , mais elles ont des bizarreries - comme avoir à la fois un +0 et -0 comme valeurs distinctes. ;-)

Lorsque vous comparez des primitives flottantes (et doubles), Java traite +0 et -0 comme égaux. Mais lorsque vous les encadrez, Java les traite séparément, comme décrit dans Float#equals. Cela permet à la méthode equals d'être cohérente avec leur hashCodeimplémentation (ainsi que compareTo), qui utilise simplement les bits du float (y compris cette valeur signée) et les pousse tels quels dans un int.

Ils auraient pu choisir une autre option pour equals / hashCode / compareTo, mais ils ne l'ont pas fait. Je ne sais pas quelles étaient les considérations de conception. Mais à au moins un égard, Float#equalsallait toujours diverger de la primitive flottante ==: dans les primitives NaN != NaN, mais pour tous les objets, cela o.equals(o)doit aussi être vrai . Cela signifie que si vous l'aviez Float f = Float.NaN, alors f.equals(f)même si f.floatValue() != f.floatValue().


¹ Les valeurs NaN (pas un nombre) ont un bit de signe, mais il n'a pas d'autre signification que pour la commande, et Java l'ignore (même pour la commande).

yshavit
la source
10

Ceci est l'un des Float est égal à l'exception

il y a deux exceptions:

Si f1 représente + 0,0f tandis que f2 représente -0,0f , ou vice versa, le test égal a la valeur false

Le pourquoi est également décrit:

Cette définition permet aux tables de hachage de fonctionner correctement.

-0 et 0 seront représentés différemment en utilisant le bit 31 de Float:

Le bit 31 (le bit qui est sélectionné par le masque 0x80000000) représente le signe du nombre à virgule flottante.

Ce n'est pas le cas Integer

user7294900
la source
la question est pourquoi? Est-ce cette règle dure et rapide que nous devons entasser :(
Joker
@Joker Ajouté la citation permet aux tables de hachage de fonctionner correctement
user7294900
4
Un élément clé que cette réponse (et le javadoc) ne mentionnent pas est que la différence est que dans les flottants, +0 et -0 sont des valeurs différentes - équivalentes, mais différentes. Fondamentalement, les flotteurs ont trois parties, et la première partie est un seul bit qui indique si le flotteur est positif ou négatif. Ce n'est pas le cas pour les entiers (comme représenté en Java), qui n'ont qu'une seule valeur 0.
yshavit
@yshavit Merci, pourriez-vous s'il vous plaît partager la même chose qu'une réponse
Joker
3
@Joker Bit 31 (le bit qui est sélectionné par le masque 0x80000000) représente le signe du nombre à virgule flottante.
user7294900
5

Pour les entiers, il n'y a pas de distinction entre -0 et 0 pour les entiers car il utilise la représentation du compliment Twos . Donc, votre exemple entier iet i1sont exactement les mêmes.

Pour les flottants, il y a une représentation -0, et sa valeur est équivalente à 0, mais la représentation binaire est différente. Par conséquent, le nouveau flotteur (0f) et le nouveau flotteur (-0f) auraient des représentations différentes.

Vous pouvez voir la différence dans les représentations de bits.

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

-2147483648, 0

Et si vous laissez le fpour déclarer le, -0fil sera traité comme un entier et vous ne verrez aucune différence dans la sortie.

mat
la source
Et pourtant, le flotteur primitif semble bien fonctionner avec cela. C'est ça 0.0f == -0.0f. Donc, le comportement différent n'est que dans java.lang.Float.
ivant
3
@ivant selon IEEE754, "Les opérations de comparaison normales, cependant, traitent les NaN comme non ordonnées et comparent −0 et +0 comme égales" en.m.wikipedia.org/wiki/IEEE_754
Andy Turner
@AndyTurner, oui je comprends cela. Je souligne simplement qu'en Java il y a une différence de comportement entre le type primitif float, qui est conforme à IEEE754 à cet égard et java.lang.Float, qui ne le fait pas. La différence dans la représentation des bits ne suffit donc pas à expliquer cela.
ivant