+0 y -0 muestra un comportamiento diferente para los datos int y float

16

He leído este post negativo y cero positivo .

A mi entender, el siguiente código debería dar true y true como salida.

Sin embargo, está dando falsey truecomo salida.

Estoy comparando el cero negativo con un cero 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));
      }
  }

¿Por qué tenemos un comportamiento diferente para 0's para Integery Float?

bufón
fuente
11
Si marca los javadocs, docs.oracle.com/javase/8/docs/api/java/lang/… La definición permite que las tablas hash funcionen correctamente. Además, no hay un entero -0.
mate
@matt si -0 no es entero, entonces debe evaluarse como falso ...
Joker
3
Cuando dices i2 = -i; i2 toma la representación exacta de bits de i, no hay forma de discernirlos. iy i2son exactamente lo mismo Luego, cuando crea nuevos Integercorreos electrónicos, ambos envuelven exactamente el mismo valor. I1.equals(I)será verdad
mate
1
Intenta int i = Integer.MIN_VALUE, i2 = -i;...
Holger
1
No hay, por cierto, ninguna razón para usar newpara los tipos de envoltura aquí. Solo use, por ejemploInteger i = 0, i2 = -i; System.out.println(i.equals(i2)); Float f1 = 0f, f2 = -f1; System.out.println(f1.equals(f2));
Holger, el

Respuestas:

19

Ints y flotantes son bestias bastante diferentes en Java. Los Ints se codifican como complemento de dos , que tiene un solo valor 0. Los flotantes usan IEEE 754 (la variante de 32 bits para flotantes y 64 bits para dobles). IEEE 754 es algo complejo, pero para el propósito de esta respuesta, solo necesita saber que tiene tres secciones, la primera de las cuales es un bit de signo. Eso significa que para cualquier flotador, hay una variante positiva y negativa¹. Eso incluye 0, por lo que los flotadores en realidad tienen dos valores "cero", +0 y -0.

Por otro lado, el complemento de dos que usa ints no es la única forma de codificar enteros en informática. Existen otros métodos, como el complemento de uno , pero tienen peculiaridades, como tener un +0 y -0 como valores distintos. ;-)

Cuando compara primitivas flotantes (y dobles), Java trata a +0 y -0 como iguales. Pero cuando los encajona, Java los trata por separado, como se describe en Float#equals. Esto permite que el método equals sea coherente con su hashCodeimplementación (así como también compareTo), que solo usa los bits del flotante (incluido ese valor con signo) y los inserta tal cual en un int.

Podrían haber elegido alguna otra opción para equals / hashCode / compareTo, pero no lo hicieron. No estoy seguro de cuáles fueron las consideraciones de diseño. Pero al menos en un aspecto, Float#equalssiempre iba a divergir de los primitivos flotantes ==: en primitivos NaN != NaN, pero para todos los objetos, o.equals(o)también debe ser cierto . Eso significa que si tuvieras Float f = Float.NaN, f.equals(f)aunquef.floatValue() != f.floatValue() .


Values ​​Los valores NaN (no un número) tienen un bit de signo, pero no tiene otro significado que no sea para ordenar, y Java lo ignora (incluso para ordenar).

yshavit
fuente
10

Esta es una de flotación igual a excepción

hay dos excepciones:

Si f1 representa + 0.0f mientras que f2 representa -0.0f , o viceversa, la prueba de igualdad tiene el valor falso

El por qué se describe también:

Esta definición permite que las tablas hash funcionen correctamente.

-0 y 0 se representarán de manera diferente usando el bit 31 de Float:

El bit 31 (el bit seleccionado por la máscara 0x80000000) representa el signo del número de coma flotante.

Este no es el caso en Integer

usuario7294900
fuente
pregunta es por qué? ¿Es esta regla difícil y rápida que tenemos que meter :(
Joker
@Joker Agregó que la cotización permite que las tablas hash funcionen correctamente
user7294900
44
Una pieza clave que esta respuesta (y el javadoc) no mencionan es que la diferencia es que en flotantes, +0 y -0 son valores diferentes, equivalentes, pero diferentes. Básicamente, los flotadores tienen tres partes, y la primera parte es un solo bit que dice si el flotador es positivo o negativo. Ese no es el caso de ints (como se representa en Java), que solo tienen un único valor 0.
yshavit
@yshavit Gracias, ¿podría compartir lo mismo como respuesta?
Joker
3
@Joker Bit 31 (el bit seleccionado por la máscara 0x80000000) representa el signo del número de coma flotante.
user7294900
5

Para los enteros, no hay distinción entre -0 y 0 para los enteros porque utiliza la representación de complemento de Twos . Entonces su ejemplo entero iy i1son exactamente iguales.

Para los flotantes, hay una representación -0, y su valor es equivalente a 0, pero la representación de bits es diferente. Por lo tanto, el nuevo Float (0f) y el nuevo Float (-0f) tendrían diferentes representaciones.

Puede ver la diferencia en las representaciones de bits.

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

-2147483648, 0

Y si deja de fdeclarar -0f, se tratará como un entero y no verá ninguna diferencia en la salida.

mate
fuente
Y sin embargo, el flotador primitivo parece funcionar bien con eso. Es decir 0.0f == -0.0f. Entonces, el comportamiento diferente solo está en java.lang.Float.
ivant
3
@ivant según IEEE754, "Sin embargo, las operaciones de comparación normales tratan los NaN como desordenados y comparan −0 y +0 como iguales" en.m.wikipedia.org/wiki/IEEE_754
Andy Turner
@AndyTurner, sí, lo entiendo. Solo estoy señalando que en Java hay una diferencia en el comportamiento entre el tipo primitivo float, que se ajusta a IEEE754 a este respecto y java.lang.Floatque no. Entonces, la diferencia en la representación de bits no es suficiente para explicar esto.
ivant