Por que typeof NaN retorna 'number'?

166

Apenas por curiosidade.

Não parece muito lógico que typeof NaNseja o número. Assim como NaN === NaNouNaN == NaN retornando falso, a propósito. Essa é uma das peculiaridades do javascript, ou haveria uma razão para isso?

Edit: obrigado por suas respostas. Não é uma coisa fácil de entender. Lendo as respostas e o wiki, eu entendi mais, mas ainda assim, uma frase como

Uma comparação com um NaN sempre retorna um resultado não ordenado, mesmo quando comparado a si mesmo. Os predicados de comparação são sinalização ou não sinalização, as versões de sinalização sinalizam uma exceção inválida para essas comparações. 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.

apenas mantém minha cabeça girando. Se alguém pudesse traduzir isso em linguagem legível humana (em oposição a, digamos, matemática), eu ficaria agradecido.

KooiInc
fonte
17
+1: "NaN é um número, mas não é um número. Hum ... o quê?!"
ereOn
11
Para diversão extra; (NaN! == NaN) == true
Alex K.
1
Ainda mais divertido (mas é compreensível pensar nisso, isNaN retorna um booleano): isNaN (parseInt ('nodice')) === isNaN (parseInt ('someOtherNaN')) === true;
KooiInc
1
Se estiver usando jQuery, prefiro isNumericverificar o tipo: $.isNumeric(NaN); retorna false, onde como $.type(NaN);, retorna número. api.jquery.com/jQuery.isNumeric #
Justin
3
Como matemático profissional, devo dizer que a frase tem pouco em comum com uma linguagem precisa da matemática.
Dmitri Zaitsev

Respostas:

53

Significa Não é um número. Não é uma peculiaridade do javascript, mas um princípio comum da ciência da computação.

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

Existem três tipos de operação que retornam NaN:

Operações com um NaN como pelo menos um operando

Formas indeterminadas

  • As divisões 0/0, ∞ / ∞, ∞ / −∞, −∞ / ∞ e −∞ / −∞
  • As multiplicações 0 × ∞ e 0 × −∞
  • O poder 1 ^ ∞
  • As adições ∞ + (−∞), (−∞) + ∞ e subtrações equivalentes.

Operações reais com resultados complexos:

  • A raiz quadrada de um número negativo
  • O logaritmo de um número negativo
  • A tangente de um múltiplo ímpar de 90 graus (ou π / 2 radianos)
  • O seno inverso ou cosseno de um número que é menor que -1 ou maior que +1.

Todos esses valores podem não ser os mesmos. Um teste simples para um NaN é testar value == valueé falso.

Charles Beattie
fonte
46
Um teste ainda mais simples éisNaN(value)
Alsciende 10/10
4
@Alsciende não é equivalente embora. isNaN(undefined)retorna true, mas undefined == undefinedtambém é verdade. O mesmo vale para todos os outros tipos não-numéricos, exceto null.
Andy
7
Em outras palavras, value !== valueé provavelmente a maneira mais curta de testar se valueé realmente NaN.
Andy
1
Parece que você está certo, @ Andy. Agora isso é uma peculiaridade.
Alsciende 02/09/2015
2
A frase "esses valores podem não ser os mesmos" não tem significado, porque esses valores não existem.
Dmitri Zaitsev
103

Bem, NaNainda é um tipo numérico , apesar de na verdade representar Not-A-Number :-)

NaN significa apenas que o valor específico não pode ser representado dentro das limitações do tipo numérico (embora isso possa ser dito para todos os números que precisam ser arredondados para caber, mas NaN é um caso especial).

Um específico NaNnão é considerado igual a outro NaNporque pode ter valores diferentes. No entanto, NaNainda é um tipo de número, assim como 2718 ou 31415.


Quanto à sua pergunta atualizada para explicar em termos leigos:

Uma comparação com um NaN sempre retorna um resultado não ordenado, mesmo quando comparado a si mesmo. Os predicados de comparação são sinalização ou não sinalização, as versões de sinalização sinalizam uma exceção inválida para essas comparações. 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.

Tudo isso significa é (dividido em partes):

Uma comparação com um NaN sempre retorna um resultado não ordenado, mesmo quando comparado a si mesmo.

Basicamente, a NaNnão é igual a nenhum outro número, incluindo outro NaNe até mesmo a si próprio .

Os predicados de comparação são sinalização ou não sinalização, as versões de sinalização sinalizam uma exceção inválida para essas comparações.

Tentar fazer operações de comparação (menor que, maior que etc.) entre um NaNe outro número pode resultar no lançamento de uma exceção (sinalização) ou em apenas ficar falso como resultado (sem sinalização ou silencioso).

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.

Os testes de igualdade (igual a, diferente de) nunca estão sinalizando, portanto, usá-los não causará uma exceção. Se você tiver um número regular x, x == xsempre será verdadeiro. Se xfor a NaN, x == xsempre será falso. Está lhe dando uma maneira de detectar NaNfacilmente (silenciosamente).

paxdiablo
fonte
1
boa explicação, apesar de eu discordar nas duas últimas frases: a melhor maneira de verificar se x é um NaN é usar a função isNaN ()
Carlos Barcelona
@DominicRodger, aqui está como eu penso sobre isso: typeof a === 'number'significa "a é armazenado internamente como um flutuador IEEE 754"
Andy
Por que Infinity === Infinityretornar truese um Infinitypode ser produzido por valores diferentes: 1.0 / 0.0 ou 2.0 / 0.0?
Hashem Qolami
1
@ Hashem, muito provavelmente porque são considerados o mesmo infinito. Tratando a divisão como subtração repetida, não faz diferença se você começa com duas ou uma, é o mesmo número de etapas necessárias para atingir (ou, mais precisamente, não atingir) zero. Entendo que os gurus da matemática têm classes diferentes de infinito, mas (1) eu suspeito 1/0e 2/0pertenço à mesma classe e (2) há apenas uma classe de infinito no IEEE754 (exceto, é +/-claro).
22416
1
Não conheço nenhuma maneira de definir esses "números reais" excepcionais de maneira significativa. Em matemática, o log e a raiz dos negativos só podem ser obtidos por meio da extensão de reais para números complexos, onde eles avaliam múltiplos valores e 0/0não são definidos de maneira significativa, exceto por dizer que seu "valor" é todo o conjunto de números. E mesmo se eles foram definidos, Math.log(-1) == Math.log(-1)ainda avalia como false. Portanto, não apenas não existem "números reais", NaNmas, mesmo que existam, eles não foram usados ​​para comparação.
Dmitri Zaitsev
20

O padrão ECMAScript (JavaScript) especifica que Numberssão flutuadores IEEE 754 , que incluem NaNcomo um valor possível.

ECMA 262 5e, secção 4.3.19 : valor numérico

valor primitivo correspondente a um valor IEEE 754 de formato binário de 64 bits de precisão dupla.

ECMA 262 5e, secção 4.3.23 : NaN

Valor numérico que é um valor "Não é um número" IEEE 754.

IEEE 754 na Wikipedia

O padrão IEEE para aritmética de ponto flutuante é um padrão técnico estabelecido pelo Instituto de Engenheiros Elétricos e Eletrônicos e o padrão mais amplamente utilizado para o cálculo de ponto flutuante [...]

O padrão define

  • formatos aritméticos : conjuntos de dados binários e decimais de ponto flutuante, que consistem em números finitos (incluindo zeros assinados e números subnormais), infinitos e valores especiais "não um número" (NaNs)

[...]

Jeremy Banks
fonte
8

typeof NaNretorna 'number'porque:

  • A especificação ECMAScript diz que o tipo Number inclui NaN:

    4.3.20 Tipo de número

    conjunto de todos os valores numéricos possíveis, incluindo os valores especiais "Não é um número" (NaN), infinito positivo e infinito negativo

  • Então typeofretorna em conformidade:

    11.4.3 O tipo de operador

    A produção UnaryExpression : typeof UnaryExpression é avaliada da seguinte maneira:

    1. Seja val o resultado da avaliação da UnaryExpression .
    2. Se Type ( val ) for Reference , então
      1. Se IsUnresolvableReference ( val ) for verdadeiro , retorne "undefined".
      2. Vamos Val ser GetValue ( val ).
    3. Devolver uma cadeia determinado pelo Tipo ( val ) de acordo com a Tabela 20.

                    Table 20 — typeof Operator Results
    ==================================================================
    |        Type of val         |              Result               |
    ==================================================================
    | Undefined                  | "undefined"                       |
    |----------------------------------------------------------------|
    | Null                       | "object"                          |
    |----------------------------------------------------------------|
    | Boolean                    | "boolean"                         |
    |----------------------------------------------------------------|
    | Number                     | "number"                          |
    |----------------------------------------------------------------|
    | String                     | "string"                          |
    |----------------------------------------------------------------|
    | Object (native and does    | "object"                          |
    | not implement [[Call]])    |                                   |
    |----------------------------------------------------------------|
    | Object (native or host and | "function"                        |
    | does implement [[Call]])   |                                   |
    |----------------------------------------------------------------|
    | Object (host and does not  | Implementation-defined except may |
    | implement [[Call]])        | not be "undefined", "boolean",    |
    |                            | "number", or "string".            |
    ------------------------------------------------------------------

Esse comportamento está de acordo com o padrão IEEE para aritmética de ponto flutuante (IEEE 754) :

4.3.19 Valor numérico

valor primitivo correspondente a um valor IEEE 754 de formato binário de 64 bits de precisão dupla

4.3.23 NaN

valor numérico que é um valor IEEE 754 "Não é um número"

8.5 O tipo de número

O tipo Number tem exatamente 18437736874454810627 (ou seja, 2 53 −2 64 +3) valores, representando os valores IEEE 754 de precisão dupla no formato 64 bits, conforme especificado no Padrão IEEE para aritmética binária de ponto flutuante, exceto que o 9007199254740990 ( isto é, 2 53 -2) distintos valores “não-um-número” do padrão IEEE são representados em ECMAScript como uma única especial NaN valor. (Observe que o valor NaN é produzido pela expressão do programa NaN.)

Oriol
fonte
5

NaN é um valor de ponto flutuante válido ( http://en.wikipedia.org/wiki/NaN )

e NaN === NaN é falso porque eles não são necessariamente o mesmo não número

Charles Ma
fonte
1
Desculpe, mas tenho que dizer que não é uma boa maneira de pensar sobre isso. "não necessariamente o mesmo não-número" não significa que eles sempre sejam diferentes e compará-los deve resultar em falso. É melhor não quantificar NaN e apenas pensar nisso como uma singularidade em nossa base de conhecimento.
Dave
1
Então, por que todos Infinitysão idênticos? Alguma ideia?
Hashem Qolami
5

NaN != NaNporque eles não são necessários o mesmo número não. Portanto, faz muito sentido ... Também porque os carros alegóricos têm +0,00 e -0,00 que não são iguais. O arredondamento pode fazer com que eles não sejam realmente zero.

Quanto ao typeof, isso depende do idioma. E a maioria dos idiomas dirá que NaN é um número flutuante, duplo ou número, dependendo de como eles o classificam ... Não conheço idiomas que digam que esse é um tipo desconhecido ou nulo.

Cine
fonte
1
ehr, considere: var x = parseInt ('sem dados'), y = x; Agora eu diria que os dois NaN são exatamente iguais? Mas não, x === y também retorna falso.
KooiInc
sim, mas você não pode ter certeza e, portanto, eles não são os mesmos. É a mesma lógica que a lógica NULLable no banco de dados. Embora muitas pessoas pensem nelas como ponteiros nulos de outras linguagens de programação, elas têm uma semântica totalmente diferente. Eles são "DESCONHECIDOS" e, portanto, um valor NULL comparado a outro é sempre falso. Fazer cálculos em um valor NULL termina com o resultado NULL. Tente vê-lo a partir da perspectiva do valor que está sendo chamado DESCONHECIDO vez
Cine
Como sendo do tipo number, NaNé primitivo e, portanto, determinado exclusivamente por seu valor.
Dmitri Zaitsev
4

NaNsignifica Não é um número . É um valor de tipos de dados numéricos (geralmente tipos de ponto flutuante, mas nem sempre) que representa o resultado de uma operação inválida, como dividir por zero.

Embora seus nomes digam que não é um número, o tipo de dados usado para retê-lo é um tipo numérico. Assim, em JavaScript, a solicitação do tipo de dados NaNretornará number(como alert(typeof(NaN))demonstra claramente).

Joachim Sauer
fonte
Na verdade, dividir por zero é avaliado como Infinitynão #NaN
Dmitri Zaitsev
2

Javascript usa NaN para representar qualquer coisa que encontrar e que não possa ser representada de nenhuma outra maneira por suas especificações. Isso não significa que não é um número. É apenas a maneira mais fácil de descrever o encontro. NaN significa que ele ou um objeto que se refere a ele não pode ser representado de nenhuma outra maneira por javascript. Para todos os fins práticos, é "desconhecido". Sendo "desconhecido", não pode dizer o que é nem mesmo se é ele próprio. Não é nem o objeto ao qual está atribuído. Ele pode apenas dizer o que não é, e não-nada ou nada só podem ser descritos matematicamente em uma linguagem de programação. Como a matemática é sobre números, o javascript não representa nada como NaN. Isso não significa que não é um número. Isso significa que não podemos ler de outra maneira que faça sentido. É por isso que pode " nem se iguala. Porque não.

Louis
fonte
2

Um nome melhor para NaN, descrevendo seu significado de maneira mais precisa e menos confusa, seria uma exceção numérica . É realmente outro tipo de objeto de exceção disfarçado de tipo primitivo (pelo design da linguagem), onde, ao mesmo tempo, não é tratado como primitivo em sua falsa auto-comparação. De onde a confusão. E enquanto a linguagem "não se decidir" em escolher entre o objeto de exceção apropriado e o numeral primitivo , a confusão permanecerá.

A infame não igualdade de NaNsi mesma, ambas ==e ===é uma manifestação do design confuso que força esse objeto de exceção a ser um tipo primitivo. Isso quebra o princípio fundamental de que um primitivo é determinado exclusivamente por seu valor . Se NaNpreferir ser visto como exceção (da qual pode haver tipos diferentes), não deve ser "vendido" como primitivo. E se se deseja que seja primitivo, esse princípio deve se manter. Enquanto estiver quebrado, como temos em JavaScript, e não podemos realmente decidir entre os dois, a confusão que leva a uma carga cognitiva desnecessária para todos os envolvidos permanecerá. O que, no entanto, é realmente fácil de corrigir, basta escolher entre os dois:

  • crie NaNum objeto de exceção especial contendo as informações úteis sobre como a exceção surgiu, em vez de jogar fora essas informações como o que está implementado atualmente, levando a um código mais difícil de depurar;
  • ou criar NaNuma entidade do tipo primitivo number(que poderia ser menos confundidamente chamado de "numérico"); nesse caso, deve ser igual a si mesmo e não pode conter outras informações; o último é claramente uma escolha inferior.

A vantagem única concebível de forçar NaNem numbertipo é ser capaz de jogá-lo de volta para qualquer expressão numérica. O que, no entanto, torna a opção frágil, porque o resultado de qualquer expressão numérica que contenha NaNserá NaNou levará a resultados imprevisíveis, como NaN < 0avaliar false, ou seja, retornar em booleanvez de manter a exceção.

E mesmo que "as coisas sejam do jeito que são", nada nos impede de fazer essa distinção clara para nós mesmos, para ajudar a tornar nosso código mais previsível e mais fácil de depurar. Na prática, isso significa identificar essas exceções e lidar com elas como exceções. O que, infelizmente, significa mais código, mas espero que seja atenuado por ferramentas como o TypeScript do Flowtype.

E então nós temos a distinção bagunçada silenciosa vs barulhenta, também conhecida como sinalizaçãoNaN . O que realmente é sobre como as exceções são tratadas, não as próprias exceções e nada diferente de outras exceções.

Da mesma forma, Infinitye +Infinitysão elementos do tipo numérico que surgem na extensão da linha real, mas não são números reais. Matematicamente, eles podem ser representados por sequências de números reais convergindo para um +ou para -Infinity.

Dmitri Zaitsev
fonte
1

Isso é simplesmente porque NaNé uma propriedade do objeto Number em JS. Ele não tem nada a ver com ser um número.

Nullw0rm
fonte
Como qualquer outro objeto, Number pode ter qualquer tipo de propriedade. Number.fu = "bar"; alert(typeof Number.fu);
Alsciende
NaNnão é o valor armazenado Number.NaN, qualquer que seja. NaNé um valor primitivo do tipo Number. Além disso, o valor de Number.NaNé NaN, mas isso não está relacionado.
Oriol
1

A melhor maneira de pensar em NAN é que não é conhecido número . É por isso que NAN! = NAN, porque cada valor NAN representa algum número desconhecido exclusivo. NANs são necessários porque os números de ponto flutuante têm um intervalo limitado de valores. Em alguns casos, o arredondamento ocorre onde os bits inferiores são perdidos, o que leva ao que parece ser um absurdo como 1,0 / 11 * 11! = 1,0. Valores realmente grandes e maiores são as NANs, com o infinito sendo um exemplo perfeito.

Dado que temos apenas dez dedos, qualquer tentativa de mostrar valores maiores que 10 é impossível, o que significa que esses valores devem ser NANs porque perdemos o valor real desse valor maior que 10. O mesmo vale para valores de ponto flutuante, em que o valor excede os limites do que pode ser mantido em um flutuador.

mP.
fonte
O infinito não é representado por um NaN. A tentativa de representar um número fora do intervalo seria arredondada para baixo (para max / -inf) ou para cima (para min / + inf).
OrangeDog
1

Porque NaN é um tipo de dados numérico.

BenM
fonte
1

NaNé um número do ponto de vista do tipo, mas não é um número normal como 1, 2 ou 329131. O nome "Não é um número" refere-se ao fato de que o valor representado é especial e se refere ao domínio de especificação do formato IEEE, não domínio da linguagem javascript.

6502
fonte
1

Se estiver usando jQuery, prefiro isNumericverificar o tipo:

console.log($.isNumeric(NaN));  // returns false
console.log($.type(NaN));       // returns number

http://api.jquery.com/jQuery.isNumeric/

Justin
fonte
Obrigado cara. Eu tive problemas com isNumbera partir utildo pacote de Dactilografado. Bom que ainda usamos jQueryem nosso projeto, usei sua sugestão.
Ashok MA
Para rec, isNumberfrom from utiltypescript também retorna truepara NaN.
Ashok MA
0

Javascript possui apenas um tipo de dados numérico, que é o padrão de precisão dupla de 64 bits. Tudo é duplo. NaN é um valor especial de double, mas é um double, no entanto.

Tudo o que parseIntfaz é "converter" sua string em um tipo de dados numérico, para que o resultado seja sempre "number"; somente se a sequência original não for analisável, seu valor será NaN.

Kerrek SB
fonte
0

NaN ainda é um tipo numérico, mas representa um valor que não pôde representar um número válido.

Matthew Abbott
fonte
0

Poderíamos argumentar que NaN é um objeto de caso especial. Nesse caso, o objeto do NaN representa um número que não faz sentido matemático. Existem outros objetos especiais de caso em matemática, como INFINITE e assim por diante.

Você ainda pode fazer alguns cálculos com isso, mas isso produzirá comportamentos estranhos.

Mais informações aqui: http://www.concentric.net/~ttwang/tech/javafloat.htm (baseado em java, não javascript)

Pedro Loureiro
fonte
0

Você precisa amar o Javascript. Tem algumas peculiaridades interessantes.

http://wtfjs.com/page/13

A maioria dessas peculiaridades pode ser explicada se você parar para resolvê-las logicamente, ou se você conhece um pouco da teoria dos números, mas, mesmo assim, elas ainda podem capturá-lo se você não souber sobre elas.

A propósito, eu recomendo a leitura do restante de http://wtfjs.com/ - há muitas peculiaridades mais interessantes do que essa!

Spudley
fonte
0

O valor NaN é realmente o Number.NaN, portanto, quando você pergunta se é um número, ele diz que sim. Você fez a coisa correta usando a chamada isNaN ().

Para obter informações, o NaN também pode ser retornado por operações em Números que não são definidos como divisões por zero ou raiz quadrada de um número negativo.

Roubar
fonte
Em que sentido é "o valor"? NaN == Number.NaNavalia para false!
Dmitri Zaitsev
@DmitriZaitsev Você leu o tópico? E você tentou se (parseInt ("nan") == Number.NaN)? Tente também! = E veja o que ele diz.
21716 Rob
Desculpe, esqueci o NaN==NaNser estúpido false, deve ter sido um sádico que inventou isso para fazer todo mundo sofrer.
Dmitri Zaitsev
0

Um exemplo

Imagine Estamos convertendo uma string para um número:

Number("string"); // returns NaN

Alteramos o tipo de dados para número, mas seu valor não é um número!

Amir Fo
fonte
Você parece ter perdido o objetivo da pergunta. NaNé do tipo de número . A questão está se perguntando o porquê.
Quentin
@ Quentin expliquei na última linha.
Amir Fo
-1

É um valor especial do tipo Número como POSITIVE_INFINITY

Por quê? Por design

RiaD
fonte