Por que Double.NaN == Double.NaN retorna false?

155

Eu estava apenas estudando questões da OCPJP e encontrei esse código estranho:

public static void main(String a[]) {
    System.out.println(Double.NaN==Double.NaN);
    System.out.println(Double.NaN!=Double.NaN);
}

Quando executei o código, obtive:

false
true

Como está o resultado falsequando comparamos duas coisas que parecem iguais? O que NaNsignifica isso ?

Maverick
fonte
8
Isso é realmente estranho. Como Double.NaN é estático final, a comparação com == deve retornar true. +1 para a pergunta.
Stephan
2
O mesmo acontece em python:In [1]: NaN==NaN Out[1]: False
tdc
58
O mesmo ocorre em todos os idiomas que seguem corretamente o padrão IEEE 754.
precisa saber é o seguinte
4
Intuição: "Olá" não é um número, verdadeiro (booleano) também não é um número. ! NaN = NaN pela mesma razão "Olá" = true
Kevin
3
@ Stephan: A comparação com Double.NaN==Double.NaNdeve realmente retornar true se Double.NaNfosse do tipo java.lang.Double. No entanto, seu tipo é o primitivo double, e as regras do operador para doubleaplicação (que exigem essa desigualdade por conformidade com o IEEE 754, conforme explicado nas respostas).
sleske

Respostas:

139

NaN significa "Não é um número".

A terceira edição da Java Language Specification (JLS) diz :

Uma operação que transborda produz um infinito assinado, uma operação que transborda produz um valor desnormalizado ou um zero assinado e uma operação que não possui resultado matematicamente definido produz NaN. Todas as operações numéricas com NaN como um operando produzem NaN como resultado. Como já foi descrito, o NaN não é ordenado; portanto, uma operação de comparação numérica envolvendo um ou dois retornos de NaNs falsee qualquer !=comparação envolvendo retornos de NaN true, incluindo x!=xquando xé NaN.

Adrian Mitev
fonte
4
@ nibot: Principalmente verdade . Qualquer comparação com um flutuador em conformidade com IEEE produzirá false. Portanto, esse padrão difere do Java, pois o IEEE exige isso (NAN != NAN) == false.
Tirou Dormann
2
Abrindo a caixa deste Pandora - onde você vê que "o IEEE exige que (NAN! = NAN) == false"?
Supervisor
62

NaN, por definição, não é igual a qualquer número, incluindo NaN. Isso faz parte do padrão IEEE 754 e implementado pela CPU / FPU. Não é algo que a JVM precise adicionar lógica para suportar.

http://en.wikipedia.org/wiki/NaN

Uma comparação com um NaN sempre retorna um resultado não ordenado, mesmo quando comparado a si mesmo. ... Os predicados de igualdade e desigualdade são sem sinalização, portanto x = x retornando false pode ser usado para testar se x é um NaN silencioso.

Java trata todo NaN como NaN silencioso.

Peter Lawrey
fonte
1
Ele é implementado pela CPU ou é conectado na JVM, como menciona a Bohemian?
Naweed Chougle
3
A JVM deve chamar o que quer que seja implementado corretamente. Em um PC, a CPU faz todo o trabalho como tal. Em uma máquina sem esse suporte, a JVM deve implementá-lo. (Eu não sei de qualquer máquina)
Peter Lawrey
No dia em que o 8087 era uma opção, a biblioteca C continha um emulador de FP. Programas como a JVM não precisariam se preocupar com isso de qualquer maneira.
Marquês de Lorne
49

Por que essa lógica

NaNmeios Not a Number. O que não é um número? Qualquer coisa. Você pode ter qualquer coisa de um lado e qualquer coisa do outro lado; portanto, nada garante que ambos sejam iguais. NaNé calculado com Double.longBitsToDouble(0x7ff8000000000000L)e como você pode ver na documentação de longBitsToDouble:

Se o argumento for qualquer valor no intervalo 0x7ff0000000000001Latravés 0x7fffffffffffffffLou no intervalo 0xfff0000000000001Latravés 0xffffffffffffffffL, o resultado é um NaN.

Além disso, NaNé tratado logicamente dentro da API.


Documentação

/** 
 * A constant holding a Not-a-Number (NaN) value of type
 * {@code double}. It is equivalent to the value returned by
 * {@code Double.longBitsToDouble(0x7ff8000000000000L)}.
 */
public static final double NaN = 0.0d / 0.0;

A propósito, NaN é testado como seu exemplo de código:

/**
 * Returns {@code true} if the specified number is a
 * Not-a-Number (NaN) value, {@code false} otherwise.
 *
 * @param   v   the value to be tested.
 * @return  {@code true} if the value of the argument is NaN;
 *          {@code false} otherwise.
 */
static public boolean isNaN(double v) {
    return (v != v);
}

Solução

O que você pode fazer é usar compare/ compareTo:

Double.NaNé considerado por esse método igual a si mesmo e maior que todos os outros doublevalores (inclusive Double.POSITIVE_INFINITY).

Double.compare(Double.NaN, Double.NaN);
Double.NaN.compareTo(Double.NaN);

Ou equals:

Se thise argumentambos representam Double.NaN, o equalsmétodo retorna true, mesmo que Double.NaN==Double.NaNtenha o valor false.

Double.NaN.equals(Double.NaN);
falsarella
fonte
Você conhece algum caso em que NaN != NaNser falso tornaria os programas mais complicados do que NaN != NaNverdadeiros? Sei que o IEEE tomou a decisão há muito tempo, mas, de uma perspectiva prática, nunca vi casos em que isso fosse útil. Se uma operação for executada até que iterações consecutivas produzam o mesmo resultado, ter duas iterações consecutivas produzindo NaN seria "naturalmente" detectado como uma condição de saída, não fosse por esse comportamento.
Supercat 7/09
@supercat Como você pode dizer que dois números não aleatórios são naturalmente iguais? Ou diga, primitivamente igual? Pense no NaN como uma instância, não como algo primitivo. Cada resultado anormal diferente é uma instância diferente de algo estranho e, mesmo que ambos representem o mesmo, usar == para instâncias diferentes deve retornar false. Por outro lado, ao usar iguais, ele pode ser tratado adequadamente como você deseja. [ docs.oracle.com/javase/7/docs/api/java/lang/…
falsarella 9/09/13
@falsarella: A questão não é se dois números aleatórios devem ser considerados "definitivamente iguais", mas em que casos é útil comparar qualquer número como "definitivamente desigual". Se alguém estiver tentando calcular o limite de f(f(f...f(x))), e encontrar um y=f[n](x)para alguns de nmodo que o resultado de f(y)seja indistinguível de y, então yserá indistinguível do resultado de qualquer aninhamento mais profundo f(f(f(...f(y))). Mesmo se alguém quisesse NaN==NaNser falso, Nan!=Nan também ser falso seria menos "surpreendente" do que x!=xser verdadeiro para alguns x.
Supercat 11/09/13
1
@ Falsarella: Eu acredito que o tipo de Double.NaNnão é Double, mas double, então a questão é uma relacionada ao comportamento de double. Embora existam funções que podem testar uma relação de equivalência envolvendo doublevalores, a única resposta convincente que conheço para "por que" (que faz parte da pergunta original) é "porque algumas pessoas no IEEE não acharam que o teste de igualdade deveria defina uma relação de equivalência ". BTW, existe alguma maneira idiomática concisa de testar xe yequivalência usando apenas operadores primitivos? Todas as formulações que conheço são bastante desajeitadas.
Supercat 11/09/13
1
melhor e simples resposta. Obrigado
Tarun Nagpal
16

Pode não ser uma resposta direta à pergunta. Mas se você quiser verificar se algo é igual a Double.NaNvocê, use este:

double d = Double.NaN
Double.isNaN(d);

Isso retornará true

JREN
fonte
6

O javadoc para Double.NaN diz tudo:

Uma constante contendo um valor Não-um-Número (NaN) do tipo double. É equivalente ao valor retornado por Double.longBitsToDouble(0x7ff8000000000000L).

Curiosamente, a fonte para Doubledefine NaNassim:

public static final double NaN = 0.0d / 0.0;

O comportamento especial que você descreve é ​​conectado à JVM.

Boêmio
fonte
5
É com fio na JVM ou é implementado pela CPU como Peter menciona?
Naweed Chougle
4

conforme o padrão IEEE para aritmética de ponto flutuante para números de precisão dupla,

A representação padrão de ponto flutuante de precisão dupla IEEE requer uma palavra de 64 bits, que pode ser representada como numerada de 0 a 63, da esquerda para a direita

insira a descrição da imagem aqui Onde,

S: Sign  1 bit
E: Exponent  11 bits
F: Fraction  52 bits 

Se E=2047(todos Esão 1) e Ffor diferente de zero, então V=NaN("Não é um número")

Que significa,

Se todos os Ebits forem 1 e se houver um bit diferente de zero F, o número será NaN.

portanto, entre outros, todos os números a seguir são NaN,

0 11111111 0000000000000000010000000000000000000000000000000000 = NaN
1 11111111 0000010000000000010001000000000000001000000000000000 = NaN
1 11111111 0000010000011000010001000000000000001000000000000000 = NaN

Em particular, você não pode testar

if (x == Double.NaN) 

para verificar se um resultado específico é igual Double.NaN, porque todos os valores "não são números" são considerados distintos. No entanto, você pode usar o Double.isNaNmétodo:

if (Double.isNaN(x)) // check whether x is "not a number"
Sufiyan Ghori
fonte
3

NaN é um valor especial que indica "não é um número"; é o resultado de certas operações aritméticas inválidas, como sqrt(-1), e possui a propriedade (às vezes irritante) disso NaN != NaN.

Fred Foo
fonte
2

Nenhum número representa o resultado de operações cujo resultado não é representável com um número. A operação mais famosa é 0/0, cujo resultado não é conhecido.

Por esse motivo, NaN não é igual a nada (incluindo outros valores que não são um número). Para mais informações, basta verificar a página da wikipedia: http://en.wikipedia.org/wiki/NaN

Matteo
fonte
-1: não representa o resultado de 0/0. 0/0é sempre NaN, mas NaN pode ser o resultado de outras operações - como 2+NaN: an operation that has no mathematically definite result produces NaN, conforme a resposta por @AdrianMitev
ANeves
De fato, NaN significa "Não é um número" e é o resultado de todas as operações que têm como resultado um valor indefinido ou irrepresentável. A operação mais famosa e comum é 0/0, mas obviamente existem muitas outras operações com o mesmo resultado. Concordo que minha resposta poderia ser melhorada, mas discordo do -1 ... Acabei de verificar que a wikipedia também usa as operações 0/0 como o primeiro exemplo de operação com resultado NaN ( en.wikipedia.org/wiki/ NaN ).
Matteo
Além disso, isso está na fonte Java para Double: public static final double NaN = 0.0d / 0.0;
Guillaume
1
@ Matteo +0, agora que a declaração falsa se foi. E meu -1 ou +1 não é para você concordar ou discordar; mas é bom deixar um comentário com -1, para que o autor possa entender por que sua resposta é considerada inútil - e alterá-la, se assim o desejar.
ANeves
@ Guillaume se esse comentário foi feito para mim, por favor, reformule-o: Eu não entendo.
ANeves
0

De acordo com este link , possui várias situações e difíceis de lembrar. É assim que eu me lembro e os distingo. NaNsignifica "matematicamente indefinido", por exemplo: "o resultado de 0 dividido por 0 é indefinido" e, como é indefinido, portanto "a comparação relacionada com o indefinido é obviamente indefinida". Além disso, funciona mais como premissas matemáticas. Por outro lado, tanto o infinito positivo quanto o negativo são predefinidos e definitivos, por exemplo "infinito positivo ou negativo grande é bem definido matematicamente".

Tiina
fonte