Em Java, o que significa NaN?

107

Tenho um programa que tenta reduzir a doubleaté um número desejado. A saída que recebo é NaN.

O que NaNsignifica em Java?

David
fonte
Há uma boa descrição de NaN e das armadilhas comuns ao usar NaN em Java: ppkwok.blogspot.co.uk/2012/11/…
Phil
Se você está perguntando "para que serve o NaN?" em Java (ou qualquer outra linguagem), posso dar-lhe um caso de uso em que é muito útil: quando tenho uma matriz 2-D de flutuadores, mas meu cálculo não tem valor significativo para alguma parte dessa matriz 2-D, Vou preencher esse valor com "NaN". Isso pode ser usado para sinalizar aos usuários downstream do meu cálculo (como quando ele é transformado em uma imagem raster) "não preste atenção ao valor neste momento". Muito útil!
Dan H
BTW, o que - exatamente - significa "encolher" um duplo? Curioso ...
Dan H

Respostas:

153

Retirado desta página :

"NaN" significa "não é um número". "Nan" é produzido se uma operação de ponto flutuante tiver alguns parâmetros de entrada que fazem com que a operação produza algum resultado indefinido. Por exemplo, 0,0 dividido por 0,0 é aritmeticamente indefinido. Tirar a raiz quadrada de um número negativo também é indefinido.

KennyDeriemaeker
fonte
16
Além disso, o NaN é definido pelo Padrão IEEE para Aritmética de Ponto Flutuante (IEEE 754) de forma bastante explícita, que Java segue cegamente. Ler o padrão abre seus olhos para muitas coisas, os múltiplos valores de zero sendo uma delas.
Esko
37
Além disso, NaNtem a interessante propriedade de ser o único "número" que não é igual a si mesmo quando comparado. Portanto, um comum (e em muitas línguas a única) de teste se um número xé NaNo seguinte:boolean isNaN(x){return x != x;}
quazgar
3
O link em resposta está morto?
Pang
3
... "Tirar a raiz quadrada de um número negativo é indefinido (em aritmética)" ... Não é! é na verdade ie algumas linguagens como o python lidam muito bem com isso ... Pode não ser o caso em javatu
Rafael T
5
@RafaelT Eu diria que é indefinido na aritmética não complexa. Não há como atribuir um número complexo a um número flutuante ou duplo em Java. Python é digitado dinamicamente, portanto, pode ser apenas para retornar um número complexo neste caso.
sstn
19

NaNsignifica “Não é um número” e é basicamente uma representação de um valor de ponto flutuante especial no padrão de ponto flutuante IEE 754 . NaN geralmente significa que o valor é algo que não pode ser expresso com um número de ponto flutuante válido.

Uma conversão resultará neste valor, quando o valor que está sendo convertido é outro, por exemplo, ao converter uma string que não representa um número.

cutucar
fonte
Convertendo como? Com parseFloat()ou parseDouble? Ou alguma outra coisa?
Alonso del Arte
14

NaNsignifica "Não é um número" e é o resultado de operações indefinidas em números de ponto flutuante como, por exemplo, dividir zero por zero. (Observe que, embora dividir um número diferente de zero por zero também seja geralmente indefinido em matemática, isso não resulta em NaN, mas em infinito positivo ou negativo).

sepp2k
fonte
5

NaNsignifica "Não é um número". É um valor de ponto flutuante especial que significa que o resultado de uma operação não foi definido ou não pode ser representado como um número real.

Veja aqui mais explicações sobre este valor.

Mike Daniels
fonte
4

NaN = Não é um número.

Fitzchak Yitzchaki
fonte
4

Significa não um número. É uma representação comum para um valor numérico impossível em muitas linguagens de programação.

Ibedogni
fonte
4

Exemplo de execução mínima

A primeira coisa que você precisa saber é que o conceito de NaN é implementado diretamente no hardware da CPU.

Todas as principais CPUs modernas parecem seguir IEEE 754 que especifica formatos de ponto flutuante, e NaNs, que são apenas valores flutuantes especiais, fazem parte desse padrão.

Portanto, o conceito será muito semelhante em qualquer linguagem, incluindo Java, que apenas emite código de ponto flutuante diretamente para a CPU.

Antes de continuar, você pode querer ler primeiro as seguintes respostas que escrevi:

Agora, para alguma ação Java. A maioria das funções de interesse que não estão na linguagem central estão internamente java.lang.Float.

Nan.java

import java.lang.Float;
import java.lang.Math;

public class Nan {
    public static void main(String[] args) {
        // Generate some NaNs.
        float nan            = Float.NaN;
        float zero_div_zero  = 0.0f / 0.0f;
        float sqrt_negative  = (float)Math.sqrt(-1.0);
        float log_negative   = (float)Math.log(-1.0);
        float inf_minus_inf  = Float.POSITIVE_INFINITY - Float.POSITIVE_INFINITY;
        float inf_times_zero = Float.POSITIVE_INFINITY * 0.0f;
        float quiet_nan1     = Float.intBitsToFloat(0x7fc00001);
        float quiet_nan2     = Float.intBitsToFloat(0x7fc00002);
        float signaling_nan1 = Float.intBitsToFloat(0x7fa00001);
        float signaling_nan2 = Float.intBitsToFloat(0x7fa00002);
        float nan_minus      = -nan;

        // Generate some infinities.
        float positive_inf   = Float.POSITIVE_INFINITY;
        float negative_inf   = Float.NEGATIVE_INFINITY;
        float one_div_zero   = 1.0f / 0.0f;
        float log_zero       = (float)Math.log(0.0);

        // Double check that they are actually NaNs.
        assert  Float.isNaN(nan);
        assert  Float.isNaN(zero_div_zero);
        assert  Float.isNaN(sqrt_negative);
        assert  Float.isNaN(inf_minus_inf);
        assert  Float.isNaN(inf_times_zero);
        assert  Float.isNaN(quiet_nan1);
        assert  Float.isNaN(quiet_nan2);
        assert  Float.isNaN(signaling_nan1);
        assert  Float.isNaN(signaling_nan2);
        assert  Float.isNaN(nan_minus);
        assert  Float.isNaN(log_negative);

        // Double check that they are infinities.
        assert  Float.isInfinite(positive_inf);
        assert  Float.isInfinite(negative_inf);
        assert !Float.isNaN(positive_inf);
        assert !Float.isNaN(negative_inf);
        assert one_div_zero == positive_inf;
        assert log_zero == negative_inf;
            // Double check infinities.

        // See what they look like.
        System.out.printf("nan            0x%08x %f\n", Float.floatToRawIntBits(nan           ), nan           );
        System.out.printf("zero_div_zero  0x%08x %f\n", Float.floatToRawIntBits(zero_div_zero ), zero_div_zero );
        System.out.printf("sqrt_negative  0x%08x %f\n", Float.floatToRawIntBits(sqrt_negative ), sqrt_negative );
        System.out.printf("log_negative   0x%08x %f\n", Float.floatToRawIntBits(log_negative  ), log_negative  );
        System.out.printf("inf_minus_inf  0x%08x %f\n", Float.floatToRawIntBits(inf_minus_inf ), inf_minus_inf );
        System.out.printf("inf_times_zero 0x%08x %f\n", Float.floatToRawIntBits(inf_times_zero), inf_times_zero);
        System.out.printf("quiet_nan1     0x%08x %f\n", Float.floatToRawIntBits(quiet_nan1    ), quiet_nan1    );
        System.out.printf("quiet_nan2     0x%08x %f\n", Float.floatToRawIntBits(quiet_nan2    ), quiet_nan2    );
        System.out.printf("signaling_nan1 0x%08x %f\n", Float.floatToRawIntBits(signaling_nan1), signaling_nan1);
        System.out.printf("signaling_nan2 0x%08x %f\n", Float.floatToRawIntBits(signaling_nan2), signaling_nan2);
        System.out.printf("nan_minus      0x%08x %f\n", Float.floatToRawIntBits(nan_minus     ), nan_minus     );
        System.out.printf("positive_inf   0x%08x %f\n", Float.floatToRawIntBits(positive_inf  ), positive_inf  );
        System.out.printf("negative_inf   0x%08x %f\n", Float.floatToRawIntBits(negative_inf  ), negative_inf  );
        System.out.printf("one_div_zero   0x%08x %f\n", Float.floatToRawIntBits(one_div_zero  ), one_div_zero  );
        System.out.printf("log_zero       0x%08x %f\n", Float.floatToRawIntBits(log_zero      ), log_zero      );

        // NaN comparisons always fail.
        // Therefore, all tests that we will do afterwards will be just isNaN.
        assert !(1.0f < nan);
        assert !(1.0f == nan);
        assert !(1.0f > nan);
        assert !(nan == nan);

        // NaN propagate through most operations.
        assert Float.isNaN(nan + 1.0f);
        assert Float.isNaN(1.0f + nan);
        assert Float.isNaN(nan + nan);
        assert Float.isNaN(nan / 1.0f);
        assert Float.isNaN(1.0f / nan);
        assert Float.isNaN((float)Math.sqrt((double)nan));
    }
}

GitHub upstream .

Correr com:

javac Nan.java && java -ea Nan

Resultado:

nan            0x7fc00000 NaN
zero_div_zero  0x7fc00000 NaN
sqrt_negative  0xffc00000 NaN
log_negative   0xffc00000 NaN
inf_minus_inf  0x7fc00000 NaN
inf_times_zero 0x7fc00000 NaN
quiet_nan1     0x7fc00001 NaN
quiet_nan2     0x7fc00002 NaN
signaling_nan1 0x7fa00001 NaN
signaling_nan2 0x7fa00002 NaN
nan_minus      0xffc00000 NaN
positive_inf   0x7f800000 Infinity
negative_inf   0xff800000 -Infinity
one_div_zero   0x7f800000 Infinity
log_zero       0xff800000 -Infinity

Assim, aprendemos algumas coisas:

  • operações flutuantes estranhas que não têm nenhum resultado lógico fornecem NaN:

    • 0.0f / 0.0f
    • sqrt(-1.0f)
    • log(-1.0f)

    gerar a NaN.

    Em C, é realmente possível solicitar o aumento de sinais em tais operações feenableexceptpara detectá-los, mas não acho que seja exposto em Java: Por que a divisão inteira por zero 1/0 dá erro, mas ponto flutuante 1 / 0,0 retorna "Inf"?

  • operações estranhas que estão no limite de mais ou menos infinito, no entanto, dão + - infinito em vez de NaN

    • 1.0f / 0.0f
    • log(0.0f)

    0.0 quase cai nesta categoria, mas provavelmente o problema é que ele poderia ir para mais ou menos infinito, então foi deixado como NaN.

  • se NaN for a entrada de uma operação flutuante, a saída também tende a ser NaN

  • existem vários valores possíveis para NaN 0x7fc00000 , 0x7fc00001, 0x7fc00002, embora x86_64 parece gerar apenas 0x7fc00000.

  • NaN e infinito têm representação binária semelhante.

    Vamos analisar alguns deles:

    nan          = 0x7fc00000 = 0 11111111 10000000000000000000000
    positive_inf = 0x7f800000 = 0 11111111 00000000000000000000000
    negative_inf = 0xff800000 = 1 11111111 00000000000000000000000
                                | |        |
                                | |        mantissa
                                | exponent
                                |
                                sign

    A partir disso, confirmamos o que IEEE754 especifica:

    • tanto NaN quanto infinitos têm expoente == 255 (todos uns)
    • infinitos têm mantissa == 0. Existem, portanto, apenas dois infinitos possíveis: + e -, diferenciados pelo bit de sinal
    • NaN tem mantissa! = 0. Portanto, existem várias possibilidades, exceto para mantissa == 0 que é infinito
  • Os NaNs podem ser positivos ou negativos (bit superior), embora isso não tenha efeito sobre as operações normais

Testado no Ubuntu 18.10 amd64, OpenJDK 1.8.0_191.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
fonte
3

Não sou um cara Java, mas em JS e outras linguagens eu uso "Not a Number", o que significa que alguma operação fez com que ele se tornasse um número inválido.

Brian Mains
fonte
3

Literalmente significa "Não é um número". Suspeito que algo está errado com seu processo de conversão.

Verifique a seção Não é um número nesta referência

Chris Thompson
fonte
3

Não é um valor de ponto flutuante válido (por exemplo, o resultado da divisão por zero)

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

Vladimir Dyuzhev
fonte
Eu discordo com esta resposta. Primeiro: "NaN" É um valor válido para um float IEEE! (Afinal, ele está definido na especificação ... então é "válido", certo?). Segundo: "divisão por zero" pode ser representada por IEEE "Infinito Positivo" ou "Infinito Negativo"; um exemplo melhor de "NaN" é "zero dividido por zero", como algumas outras respostas indicaram corretamente.
Dan H
"Valor válido" e "definido nas especificações" não são a mesma coisa. Concordado com 0/0.
Vladimir Dyuzhev