+0 e -0 mostram um comportamento diferente para dados int e float

16

Eu li este post com zero negativo e positivo .

Para meu entendimento, o código a seguir deve dar true e true como saída.

No entanto, está dando falsee truecomo saída.

Estou comparando zero negativo com um 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));
      }
  }

Por que temos um comportamento diferente para zeros para Integere Float?

Palhaço
fonte
11
Se você verificar os javadocs, docs.oracle.com/javase/8/docs/api/java/lang/… A definição permite que as tabelas de hash funcionem corretamente. Além disso, não há um número inteiro -0.
Matt
@ Matt se -0 não é inteiro, então ele deve ser avaliada como falsa ...
Joker
3
Quando você diz i2 = -i; i2 assume a representação exata de bits de i, não há como discerni-los. ie i2são exatamente iguais. Então, quando você cria novos Integers, ambos envolvem exatamente o mesmo valor. I1.equals(I)será verdade.
Matt
11
Tente int i = Integer.MIN_VALUE, i2 = -i;...
Holger
11
A propósito, não há motivo para usar newos tipos de wrapper aqui. Basta usar, por exemplo,Integer i = 0, i2 = -i; System.out.println(i.equals(i2)); Float f1 = 0f, f2 = -f1; System.out.println(f1.equals(f2));
Holger

Respostas:

19

Ints e floats são bestas bem diferentes em Java. Ints são codificados como complemento de dois , que possui um único valor 0. Os flutuadores usam IEEE 754 (a variante de 32 bits para flutuadores e 64 bits para duplos). O IEEE 754 é um pouco complexo, mas, para o propósito desta resposta, você só precisa saber que ele possui três seções, a primeira das quais é um sinal. Isso significa que para qualquer flutuação, há uma variante positiva e negativa¹. Isso inclui 0, então os flutuadores realmente têm dois valores "zero", +0 e -0.

Como um aparte, o complemento dos dois que os ints usam não é a única maneira de codificar números inteiros na ciência da computação. Existem outros métodos, como o complemento de alguns , mas eles têm peculiaridades - como ter ambos os valores +0 e -0 como valores distintos. ;-)

Quando você compara primitivas flutuantes (e duplas), Java trata +0 e -0 como iguais. Mas quando você caixa-los, Java trata-los separadamente, como descrito em Float#equals. Isso permite que o método equals seja consistente com sua hashCodeimplementação (e também compareTo), que apenas usa os bits do float (incluindo esse valor assinado) e os coloca como estão em um int.

Eles poderiam ter escolhido outra opção para equals / hashCode / compareTo, mas não o fizeram. Não sei ao certo quais eram as considerações de design. Mas, pelo menos em um aspecto, Float#equalssempre divergiria dos primitivos do float ==: nos primitivos NaN != NaN, mas para todos os objetos, o.equals(o)também deve ser verdade . Isso significa que se você tivesse Float f = Float.NaN, f.equals(f)mesmo assim f.floatValue() != f.floatValue().


Values ​​Os valores de NaN (não um número) têm um bit de sinal, mas não têm outro significado além de para pedidos e o Java os ignora (mesmo para pedidos).

yshavit
fonte
10

Este é um dos exceção de flutuação igual

existem duas exceções:

Se f1 representa + 0,0f enquanto f2 representa -0,0f , ou vice-versa, o teste igual tem o valor false

O porquê também é descrito:

Essa definição permite que as tabelas de hash funcionem corretamente.

-0 e 0 serão representados de maneira diferente usando o bit 31 do Float:

O bit 31 (o bit selecionado pela máscara 0x80000000) representa o sinal do número de ponto flutuante.

Este não é o caso em Integer

user7294900
fonte
pergunta é por que? É esta regra difícil e rápida que temos de amontoar :(
Joker
@Joker Adicionada a cotação, permite que as tabelas de hash funcionem corretamente #
user7294900
4
Uma peça chave que esta resposta (e o javadoc) não menciona é que a diferença é que, nos flutuadores, +0 e -0 são valores diferentes - equivalentes, mas diferentes. Basicamente, os carros alegóricos têm três partes, e a primeira parte é um único bit que diz se o carro alegórico é positivo ou negativo. Esse não é o caso das entradas (como representadas em Java), que possuem apenas um único valor 0.
precisa
@yshavit Obrigado, você poderia, por favor, compartilhar o mesmo que uma resposta
Joker
3
@Joker O bit 31 (o bit selecionado pela máscara 0x80000000) representa o sinal do número de ponto flutuante.
user7294900
5

Para os números inteiros, não há distinção entre -0 e 0 para números inteiros porque ele usa a representação de complemento de dois . Portanto, o seu exemplo inteiro ie i1são exatamente os mesmos.

Para os carros alegóricos, existe uma representação -0 e seu valor é equivalente a 0, mas a representação de bit é diferente. Portanto, o novo Float (0f) e o novo Float (-0f) teriam representações diferentes.

Você pode ver a diferença nas representações de bits.

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

-2147483648, 0

E se você deixar fde declarar o -0f, ele será tratado como um número inteiro e você não verá nenhuma diferença na saída.

mate
fonte
E, no entanto, o flutuador primitivo parece funcionar bem com isso. Essa é 0.0f == -0.0f. Portanto, o comportamento diferente é apenas java.lang.Float.
ivant
3
@ivant de acordo com a IEEE754, "As operações normais de comparação, no entanto, tratam os NaNs como não ordenados e comparam −0 e +0 como iguais" en.m.wikipedia.org/wiki/IEEE_754
Andy Turner
@ AndyTurner, sim, eu entendo isso. Estou apenas apontando que em Java há uma diferença no comportamento entre o tipo primitivo float, que está de acordo com o IEEE754 a esse respeito e o java.lang.Floatque não. Portanto, apenas a diferença na representação de bits não é suficiente para explicar isso.
ivant