Perché typeof NaN restituisce 'numero'?


166

Solo per curiosità.

Non sembra molto logico che typeof NaNsia il numero. Proprio come NaN === NaNo NaN == NaNrestituendo falso, comunque. È questa una delle peculiarità di JavaScript o ci sarebbe una ragione per questo?

Modifica: grazie per le risposte. Tuttavia, non è una cosa facile convincerli. Leggendo le risposte e il wiki ho capito di più, ma comunque, una frase simile

Un confronto con una NaN restituisce sempre un risultato non ordinato anche quando si confronta con se stesso. I predicati del confronto sono o di segnalazione o non di segnalazione, le versioni di segnalazione segnalano un'eccezione non valida per tali confronti. I predicati di uguaglianza e disuguaglianza non segnalano, quindi x = x return false può essere usato per verificare se x è un NaN silenzioso.

mi fa girare la testa. Se qualcuno fosse in grado di tradurre questo in un linguaggio leggibile (al contrario, per esempio, matematico), sarei grato.


17
+1: "NaN è un numero ma non è un numero. Hum ... cosa?!"
ereOn

11
Per un divertimento extra; (NaN! == NaN) == true
Alex K.

1
Ancora più divertente (ma comprensibile a pensarci, isNaN restituisce un valore booleano): isNaN (parseInt ('nodice')) === isNaN (parseInt ('someOtherNaN')) === true;
KooiInc,

1
Se si utilizza jQuery, preferisco isNumericcontrollare il tipo: $.isNumeric(NaN); restituisce false, dove as $.type(NaN);restituisce il numero. api.jquery.com/jQuery.isNumeric
Justin

3
Come matematico professionista devo dire che la frase ha poco in comune con un linguaggio preciso della matematica.
Dmitri Zaitsev,

Risposte:


53

Significa non un numero. Non è una peculiarità di JavaScript, ma un comune principio di informatica.

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

Esistono tre tipi di operazioni che restituiscono NaN:

Operazioni con un NaN come almeno un operando

Forme indeterminate

  • Le divisioni 0/0, ∞ / ∞, ∞ / −∞, −∞ / ∞ e −∞ / −∞
  • Le moltiplicazioni 0 × ∞ e 0 × −∞
  • Il potere 1 ^ ∞
  • Le aggiunte ∞ + (−∞), (−∞) + ∞ e sottrazioni equivalenti.

Operazioni reali con risultati complessi:

  • La radice quadrata di un numero negativo
  • Il logaritmo di un numero negativo
  • La tangente di un multiplo dispari di 90 gradi (o π / 2 radianti)
  • Il seno o coseno inverso di un numero che è inferiore a -1 o maggiore di +1.

Tutti questi valori potrebbero non essere uguali. Un semplice test per un NaN è testare value == valueè falso.


46
Un test ancora più semplice èisNaN(value)
Alsciende,

4
@Alsciende non è equivalente però. isNaN(undefined)ritorna true, ma undefined == undefinedè anche vero. Lo stesso vale per tutti gli altri tipi non numerici tranne null.
Andy,

7
In altre parole, value !== valueè probabilmente il modo più breve per verificare se lo valueè davvero NaN.
Andy,

1
Sembra che tu abbia ragione, @Andy. Questa è una peculiarità.
Alsciende,

2
La frase "questi valori potrebbero non essere gli stessi" non ha alcun significato, poiché tali valori non esistono.
Dmitri Zaitsev,

103

Bene, NaNè ancora un tipo numerico , nonostante in realtà sta per Not-A-Number :-)

NaNsignifica solo che il valore specifico non può essere rappresentato entro i limiti del tipo numerico (anche se ciò potrebbe essere detto per tutti i numeri che devono essere arrotondati per adattarsi, ma NaNè un caso speciale).

Uno specifico NaNnon è considerato uguale a un altro NaNperché possono essere valori diversi. Tuttavia, NaNè ancora un tipo di numero, proprio come 2718 o 31415.


Per quanto riguarda la tua domanda aggiornata per spiegare in parole povere:

Un confronto con una NaN restituisce sempre un risultato non ordinato anche quando si confronta con se stesso. I predicati del confronto sono o di segnalazione o non di segnalazione, le versioni di segnalazione segnalano un'eccezione non valida per tali confronti. I predicati di uguaglianza e disuguaglianza non segnalano, quindi x = x return false può essere usato per verificare se x è un NaN silenzioso.

Tutto ciò significa che (suddiviso in parti):

Un confronto con una NaN restituisce sempre un risultato non ordinato anche quando si confronta con se stesso.

Fondamentalmente, a NaNnon è uguale a nessun altro numero, incluso un altro NaN, e persino incluso se stesso .

I predicati del confronto sono o di segnalazione o non di segnalazione, le versioni di segnalazione segnalano un'eccezione non valida per tali confronti.

Il tentativo di eseguire operazioni di confronto (minore di, maggiore di e così via) tra un NaNnumero e un altro può comportare il lancio di un'eccezione (segnalazione) o semplicemente il risultato falso (non segnalazione o silenzioso).

I predicati di uguaglianza e disuguaglianza non segnalano, quindi x = x return false può essere usato per verificare se x è un NaN silenzioso.

I test per l'uguaglianza (uguale a, non uguale a) non segnalano mai, quindi il loro utilizzo non causerà un'eccezione. Se hai un numero normale x, allora x == xsarà sempre vero. Se xè un NaN, allora x == xsarà sempre falso. Ti sta dando un modo per rilevare NaNfacilmente (in silenzio).


1
buona spiegazione, anche se non sono d'accordo nelle ultime due frasi: un modo migliore per verificare se x è un NaN sta usando la funzione isNaN ()
Carlos Barcelona,

@DominicRodger ecco come ci penso: typeof a === 'number'significa "a è memorizzato internamente come float IEEE 754"
Andy,

Perché Infinity === Infinityritorna truese un Infinitypuò essere prodotto con valori diversi: 1.0 / 0.0 o 2.0 / 0.0?
Hashem Qolami,

1
@Hashem, molto probabilmente perché sono considerati la stessa infinità. Considerando la divisione come sottrazione ripetuta, non fa alcuna differenza se si inizia da due o uno, è lo stesso numero di passaggi necessari per raggiungere (o, più precisamente, non raggiungere) zero. Capisco che i guru della matematica abbiano diverse classi di infinito, ma (1) sospetto 1/0e 2/0giaccio nella stessa classe e (2) c'è solo una classe di infinito in IEEE754 (oltre +/-ovviamente).
paxdiablo,

1
Non sono a conoscenza di alcun modo per definire questi "numeri reali" eccezionali in alcun modo significativo. In matematica il registro e la radice dei negativi possono essere ottenuti solo mediante l'estensione dei reali a numeri complessi, in cui valutano più valori e 0/0non sono definiti in modo significativo, a parte il dire che il suo "valore" è l'intero insieme di numeri. E anche se sono stati definiti, Math.log(-1) == Math.log(-1)valuta ancora false. Quindi non solo non ci sono "numeri reali" diversi da, NaNma anche se ci fossero, non sono stati usati per il confronto.
Dmitri Zaitsev,

20

Lo standard ECMAScript (JavaScript) specifica che Numberssono float IEEE 754 , che includono NaNcome possibile valore.

ECMA 262 5e Sezione 4.3.19 : valore numerico

valore di base corrispondente a un valore IEEE 754 in formato binario a 64 bit a doppia precisione.

ECMA 262 5e Sezione 4.3.23 : NaN

Valore numerico che è un valore IEEE 754 "Non un numero".

IEEE 754 su Wikipedia

Lo standard IEEE per l'aritmetica in virgola mobile è uno standard tecnico stabilito dall'Institute of Electrical and Electronics Engineers e lo standard più ampiamente utilizzato per il calcolo in virgola mobile [...]

Lo standard definisce

  • formati aritmetici : insiemi di dati binari e decimali in virgola mobile, costituiti da numeri finiti (inclusi zeri con segno e numeri subnormali), infiniti e valori speciali "non un numero" (NaNs)

[...]


8

typeof NaNritorna 'number'perché:

  • Le specifiche ECMAScript indicano che il tipo di numero include NaN:

    4.3.20 Tipo di numero

    insieme di tutti i possibili valori numerici inclusi gli speciali valori "Non un numero" (NaN), l'infinito positivo e l'infinito negativo

  • Quindi typeofritorna di conseguenza:

    11.4.3 Il tipo di operatore

    La produzione UnaryExpression : typeof UnaryExpression viene valutata come segue:

    1. Lascia che val sia il risultato della valutazione di UnaryExpression .
    2. Se Tipo ( val ) è Riferimento , allora
      1. Se IsUnresolvableReference ( val ) è true , restituisce "undefined".
      2. Lascia che val sia GetValue ( val ).
    3. Restituire una stringa determinata dal tipo ( val ) secondo la Tabella 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".            |
    ------------------------------------------------------------------

Questo comportamento è conforme allo standard IEEE per l'aritmetica a virgola mobile (IEEE 754) :

4.3.19 Valore numerico

valore di base corrispondente a un valore IEEE 754 in formato binario a 64 bit a doppia precisione

4.3.23 NaN

valore numerico che è un valore IEEE 754 "Not-a-Number"

8.5 Il tipo di numero

Il tipo Numero ha esattamente 18437736874454810627 (ovvero 2 53 −2 64 +3) valori, che rappresentano i valori IEEE 754 in formato a 64 bit a doppia precisione, come specificato nella norma IEEE per l'aritmetica binaria a virgola mobile, tranne che per il 9007199254740990 ( vale a dire, 2 53 −2) valori “Not-a-Number” distinti dello standard IEEE sono rappresentati in ECMAScript come un singolo valore NaN speciale . (Notare che il valore NaN è prodotto dall'espressione del programma NaN.)


5

NaN è un valore in virgola mobile valido ( http://en.wikipedia.org/wiki/NaN )

e NaN === NaN è falso perché non sono necessariamente lo stesso non-numero


1
Scusa ma devo dire che non è un buon modo di pensarci. "non necessariamente lo stesso non-numero" non significa che siano sempre diversi e il loro confronto dovrebbe dare falso. È meglio non quantificare la NaN e considerarla semplicemente come una stranezza nella nostra base di conoscenze.
Dave,

1
Quindi perché tutti i Infinitys in qualche modo sono identici? qualche idea?
Hashem Qolami,

5

NaN != NaNperché non sono necessari lo STESSO non numero. Quindi ha molto senso ... Anche perché i float hanno sia +0,00 che -0,00 che non sono gli stessi. L'arrotondamento può far sì che in realtà non siano zero.

Per quanto riguarda il typeof, dipende dalla lingua. E la maggior parte delle lingue dirà che NaN è un float, doppio o numero a seconda di come lo classificano ... Non conosco lingue che diranno che questo è un tipo sconosciuto o nullo.


1
ehr, considera: var x = parseInt ('no dice'), y = x; Ora direi che entrambi i NaN sono esattamente gli stessi? Ma no, x === y restituisce anche false.
KooiInc,

sì, ma non puoi essere SICURO, e quindi non sono gli stessi. È la stessa logica della logica NULLable nel database. Sebbene molte persone li considerino uguali ai puntatori null di altri linguaggi di programmazione, hanno infatti una semantica totalmente diversa. Sono "SCONOSCIUTI" e quindi un valore NULL rispetto ad un altro è sempre falso. Fare calcoli su un valore NULL finisce con il risultato NULL. Prova invece a vederlo dal punto di vista del valore chiamato SCONOSCIUTO
Cine,

Essendo di tipo number, NaNè primitivo, e quindi determinato in modo univoco dal suo valore.
Dmitri Zaitsev

4

NaNsta per Not a Number . È un valore di tipi di dati numerici (in genere tipi a virgola mobile, ma non sempre) che rappresenta il risultato di un'operazione non valida come la divisione per zero.

Anche se il suo nome dice che non è un numero, il tipo di dati utilizzato per tenerlo è un tipo numerico. Quindi in JavaScript, la richiesta del tipo di dati di NaNrestituirà number(come alert(typeof(NaN))dimostra chiaramente).


La divisione effettiva per zero è valutata in base a InfinitynoNaN
Dmitri Zaitsev

2

Javascript utilizza NaN per rappresentare tutto ciò che incontra che non può essere rappresentato in altro modo dalle sue specifiche. Ciò non significa che non sia un numero. È solo il modo più semplice per descrivere l'incontro. NaN significa che esso o un oggetto a cui fa riferimento non possono essere rappresentati in altro modo da JavaScript. A tutti gli effetti pratici, è "sconosciuto". Essendo "sconosciuto", non può dirti di cosa si tratta e nemmeno se è se stesso. Non è nemmeno l'oggetto a cui è assegnato. Può solo dirti cosa non è, e la non-identità o il nulla possono essere descritti matematicamente solo in un linguaggio di programmazione. Poiché la matematica riguarda i numeri, javascript rappresenta il nulla come NaN. Ciò non significa che non sia un numero. Significa che non possiamo leggerlo in nessun altro modo che abbia senso. Ecco perché può ' è uguale a se stesso. Perché no.


2

Un nome migliore per NaN, descriverne il significato in modo più preciso e meno confuso, sarebbe un'eccezione numerica . È in realtà un altro tipo di oggetto di eccezione mascherato da un tipo primitivo (secondo la progettazione del linguaggio), dove allo stesso tempo non viene trattato come primitivo nel suo falso auto-confronto. Da qui la confusione. E fintanto che il linguaggio "non prenderà la sua decisione" per scegliere tra l'oggetto di eccezione appropriato e il numero primitivo , la confusione rimarrà.

La famigerata non-uguaglianza di NaNse stesso, di entrambi ==e=== è una manifestazione del disegno confuso che costringe questo oggetto d'eccezione ad essere un tipo primitivo. Ciò infrange il principio fondamentale secondo cui una primitiva è determinata unicamente dal suo valore . Se NaNsi preferisce essere visto come un'eccezione (di cui possono esserci diversi tipi), allora non dovrebbe essere "venduto" come primitivo. E se si vuole essere primitivo, tale principio deve valere. Finché è rotto, come abbiamo in JavaScript, e non possiamo davvero decidere tra i due, rimarrà la confusione che porta a un carico cognitivo non necessario per tutti i soggetti coinvolti. Che, tuttavia, è davvero facile da risolvere semplicemente facendo una scelta tra i due:

  • creare NaNun oggetto eccezione speciale contenente le informazioni utili su come è nata l'eccezione, invece di buttare via quelle informazioni come ciò che è attualmente implementato, portando a un codice più difficile da debug;
  • o crea NaNun'entità del tipo primitivonumber (che potrebbe essere meno confusamente chiamato "numerico"), nel qual caso dovrebbe essere uguale a se stesso e non può contenere altre informazioni; quest'ultima è chiaramente una scelta inferiore.

L'unico vantaggio immaginabile di forzare NaNnel numbertipo è di essere in grado di riportarlo in qualsiasi espressione numerica. Il che, tuttavia, rende la scelta fragile, perché il risultato di qualsiasi espressione numerica contenente NaNsarà o NaN, o porterà a risultati imprevedibili come la NaN < 0valutazione difalse , ovvero il ritorno booleaninvece di mantenere l'eccezione.

E anche se "le cose sono come sono", nulla ci impedisce di fare questa chiara distinzione per noi stessi, per aiutare a rendere il nostro codice più prevedibile e facilmente debuggabile. In pratica, ciò significa identificare quelle eccezioni e gestirle come eccezioni. Il che, sfortunatamente, significa più codice, ma si spera venga mitigato da strumenti come TypeScript di Flowtype.

E poi abbiamo la distinzione tra disordine silenzioso e rumoroso, ovvero segnalazioneNaN . Il che riguarda davvero il modo in cui vengono gestite le eccezioni, non le eccezioni stesse e nulla di diverso dalle altre eccezioni.

Allo stesso modo, Infinitye +Infinitysono elementi di tipo numerico che sorgono nell'estensione della linea reale ma non sono numeri reali. Matematicamente, possono essere rappresentati da sequenze di numeri reali che convergono in uno +o -Infinity.


1

Questo semplicemente perché NaNè una proprietà dell'oggetto Number in JS, non ha nulla a che fare con il fatto che è un numero.


Come ogni altro oggetto, Numero può avere qualsiasi tipo di proprietà. Number.fu = "bar"; alert(typeof Number.fu);
Alsciende,

NaNnon è il valore memorizzato Number.NaN, qualunque esso sia. NaNè un valore primitivo di tipo Numero. Inoltre, il valore di Number.NaNè NaN, ma non è correlato.
Oriol,

1

Il modo migliore per pensare a NAN è che non è un noto numero . Ecco perché NAN! = NAN perché ogni valore NAN rappresenta un numero sconosciuto univoco. I NAN sono necessari perché i numeri in virgola mobile hanno un intervallo limitato di valori. In alcuni casi si verifica un arrotondamento in cui si perdono i bit inferiori che porta a ciò che sembra essere una sciocchezza come 1.0 / 11 * 11! = 1.0. Valori veramente grandi che sono maggiori sono i NAN con l'infinito come esempio perfetto.

Dato che abbiamo solo dieci dita qualsiasi tentativo di mostrare valori maggiori di 10 è impossibile, il che significa che tali valori devono essere NAN perché abbiamo perso il vero valore di questo valore maggiore di 10. Lo stesso vale per i valori in virgola mobile, in cui il valore supera i limiti di ciò che può essere tenuto in un float.


L'infinito non è rappresentato da una NaN. Il tentativo di rappresentare un numero al di fuori dell'intervallo verrebbe arrotondato per difetto (a max / -inf) o verso l'alto (a min / + inf).
OrangeDog,

1

Perché NaN è un tipo di dati numerico.


1

NaNè un numero dal punto di vista del tipo, ma non è un numero normale come 1, 2 o 329131. Il nome "Not A Number" si riferisce al fatto che il valore rappresentato è speciale e riguarda il dominio delle specifiche del formato IEEE, non dominio della lingua javascript.


1

Se si utilizza jQuery, preferisco isNumericcontrollare il tipo:

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

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


Grazie uomo. Ho avuto problemi con isNumberdal utilpacchetto di Typescript. Bene, usiamo ancora jQuerynel nostro progetto, quindi ho usato il tuo suggerimento.
Ashok MA,

Per il rec, anche isNumberdal utildattiloscritto ritorna trueper NaN.
Ashok MA,

0

Javascript ha un solo tipo di dati numerico, che è il float standard a doppia precisione a 64 bit. Tutto è un doppio. NaN è un valore speciale di double, ma è comunque un doppio.

Tutto ciò che parseIntfa è "trasmettere" la tua stringa in un tipo di dati numerico, quindi il risultato è sempre "numero"; solo se la stringa originale non era analizzabile, il suo valore sarà NaN.


0

NaN è ancora un tipo numerico, ma rappresenta un valore che non può rappresentare un numero valido.


0

Potremmo sostenere che NaN è un oggetto caso speciale. In questo caso, l'oggetto di NaN rappresenta un numero che non ha senso matematico. Ci sono altri oggetti con casi speciali in matematica come INFINITE e così via.

Puoi ancora fare alcuni calcoli con esso, ma questo produrrà comportamenti strani.

Maggiori informazioni qui: http://www.concentric.net/~ttwang/tech/javafloat.htm (basato su Java, non JavaScript)


0

Devi amare Javascript. Ha alcune piccole stranezze interessanti.

http://wtfjs.com/page/13

La maggior parte di queste stranezze può essere spiegata se smetti di elaborarle logicamente o se conosci un po 'la teoria dei numeri, ma tuttavia possono ancora prenderti alla sprovvista se non le conosci.

A proposito, ti consiglio di leggere il resto di http://wtfjs.com/ : ci sono molte stranezze molto più interessanti di questa da trovare!


0

Il valore NaN è in realtà il Number.NaN quindi quando chiedi se è un numero ti dirà di sì. Hai fatto la cosa giusta usando la chiamata isNaN ().

Per informazione, NaN può anche essere restituito da operazioni su numeri che non sono definiti come divisioni da zero o radice quadrata di un numero negativo.


In che senso è "il valore"? NaN == Number.NaNvaluta false!
Dmitri Zaitsev

@DmitriZaitsev Hai letto la discussione? E hai provato if (parseInt ("nan") == Number.NaN)? Prova anche! = E vedi cosa ti dice.
Rob,

Scusa, ho dimenticato lo stupido NaN==NaNessere false, deve essere stato un sadico a inventarlo per far soffrire tutti.
Dmitri Zaitsev,

0

Un esempio

Immagina che stiamo convertendo una stringa in un numero:

Number("string"); // returns NaN

Abbiamo cambiato il tipo di dati in numero ma il suo valore non è un numero!


Sembra che tu abbia perso il punto della domanda. NaNè del tipo numerico . La domanda è chiedersi perché.
Quentin,

@Quentin ho spiegato all'ultima riga.
Amir Fo

-1

È un valore speciale di Tipo numero come POSITIVE_INFINITY

Perché? Di progettazione

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.