Domanda

Perché il confronto di valori NaN si comportano in modo diverso da tutti gli altri valori? Cioè, tutti i confronti con gli operatori ==, <=,> =, <,> in cui uno o entrambi i valori è NaN restituisce false, contrariamente al comportamento di tutti gli altri valori.

Suppongo che questo semplifica calcoli numerici, in qualche modo, ma non riuscivo a trovare una ragione esplicitamente indicato, nemmeno nel Lecture Notes sullo status di IEEE 754 da Kahan che discute le altre decisioni di progettazione in dettaglio.

Questo comportamento deviante sta causando problemi quando si fa semplice elaborazione dei dati. Ad esempio, quando l'ordinamento di un elenco di record w.r.t. alcuni valori reali campo in un programma C ho bisogno di scrivere codice aggiuntivo per gestire NaN come elemento massimo, altrimenti l'algoritmo di ordinamento potrebbe diventare confusa.

Modifica Le risposte finora tutto sostengono che non ha senso confrontare NaNs.

Sono d'accordo, ma questo non significa che la risposta corretta è falso, piuttosto sarebbe un non-a-booleana (NaB), che per fortuna non esiste.

Quindi, la scelta di ritornare vero o falso per i confronti, a mio avviso arbitraria, e per i dati generali elaborazione sarebbe vantaggioso se obbediva alle consuete leggi (Riflessività di ==, tricotomia di <, ==,>), per timore che le strutture di dati che si basano su queste leggi si confondono.

Così mi sto chiedendo qualche vantaggio concreto di rompere queste leggi, non solo ragionamento filosofico.

Modifica 2: Credo di capire ora perché fare NaN massima sarebbe una cattiva idea, sarebbe rovinare il calcolo dei limiti superiore.

NaN! = NaN potrebbe essere auspicabile per evitare di rilevare la convergenza in un ciclo come

while (x != oldX) {
    oldX = x;
    x = better_approximation(x);
}

che però dovrebbe meglio essere scritto confrontando la differenza assoluta con un piccolo limite. Quindi secondo me questo è un argomento relativamente debole per rompere riflessività a NaN.

È stato utile?

Soluzione

ero un membro del comitato IEEE-754, cercherò di aiutare a chiarire le cose un po '.

Prima di tutto, i numeri in virgola mobile non sono numeri reali, e in virgola mobile non soddisfano gli assiomi reali aritmetica. Trichotomy non è l'unica proprietà di aritmetica reale che non vale per i galleggianti, e neppure il più importante. Ad esempio:

  • Inoltre non è associativa.
  • La legge distributiva non regge.
  • numeri
  • virgola mobile ci sono senza inverse.

Potrei andare avanti. Non è possibile specificare un tipo aritmetico di dimensione fissa che soddisfa tutti delle proprietà di beni aritmetica che conosciamo e amiamo. Il Comitato 754 deve decidere di piegare o rompere alcune di esse. Questo è guidata da alcuni principi piuttosto semplice:

  1. Quando possiamo, noi abbinare il comportamento del vero aritmetica.
  2. Se non possiamo, cerchiamo di rendere le violazioni come prevedibile e facile da diagnosticare in quanto possibile.

Per quanto riguarda il tuo commento "che non vuol dire che la risposta corretta è falsa", questo è sbagliato. Il (y < x) predicato chiede se y è inferiore x. Se y è NaN, allora è non meno di qualsiasi virgola mobile valore x, quindi la risposta è necessariamente falso.

Ho detto che trichotomy non vale per i valori in virgola mobile. Tuttavia, v'è una struttura simile che sia in possesso. Clausola 5.11, comma 2 dello standard 754-2008:

  

Quattro relazioni reciprocamente esclusivi sono possibili: minore, uguale, maggiore di, e non ordinata. L'ultimo caso si verifica quando almeno uno degli operandi è NaN. Ogni NaN deve confrontare non ordinata con tutto, compreso se stesso.

Per quanto riguarda la scrittura di codice aggiuntivo per gestire NaNs va, di solito è possibile (anche se non sempre facile) per strutturare il codice in modo tale che NaNs cadere attraverso correttamente, ma questo non è sempre il caso. Quando non è, un po 'di codice aggiuntivo può essere necessario, ma questo è un piccolo prezzo da pagare per la comodità che la chiusura algebrica portato a virgola mobile.


Addendum: Molti commentatori hanno sostenuto che sarebbe più utile per preservare la riflessività di uguaglianza e tricotomia sulla base del fatto che l'adozione di NaN! = NaN non sembra per conservare qualsiasi assioma familiare. Confesso di avere una certa simpatia per questo punto di vista, quindi ho pensato di rivisitare questa risposta e di fornire un po 'più di contesto.

La mia comprensione di parlare con Kahan è che NaN = NaN avuto origine da due considerazioni pragmatiche:

  • Tale x == y dovrebbe essere equivalente a x - y == 0 quando possibile (oltre ad essere un teorema di beni aritmetica, questo rende implementazione hardware di confronto più efficiente dello spazio, che era di massima importanza al momento sviluppato lo standard - nota, tuttavia, che questo è violata per x = y = infinito, quindi non è un buon motivo da solo,. avrebbe potuto ragionevolmente stato piegato a (x - y == 0) or (x and y are both NaN))

  • Ancora più importante, non vi era alcuna predicato isnan( ) nel momento in cui NaN è stato formalizzato nel 8087 aritmetica; si è reso necessario per fornire ai programmatori un mezzo conveniente ed efficiente per rilevare valori NaN che non dipendono da linguaggi di programmazione che fornisce qualcosa come isnan( ) che potrebbe richiedere molti anni. Citerò propria scrittura di Kahan sul tema:

  

se non ci fosse modo per sbarazzarsi di Nans, sarebbero inutili come indefiniti su Crays; appena si sono incontrate, calcolo sarebbe meglio interrotto anziché continuato per un tempo indefinito a conclusione indefinito. È per questo che alcune operazioni su NaNs devono fornire risultati non NaN. Quali operazioni? ... Le eccezioni sono predicati C “x == x” e “x! = X”, che sono rispettivamente 1 e 0 per l'enumero molto finito o infinito x ma invertire se x non è un numero (NaN); questi forniscono l'unico semplice distinzione tra unexceptional NaNs ei numeri in lingue che non hanno una parola per Nan e un predicato isNaN (x).

Si noti che questa è anche la logica che esclude il ritorno qualcosa come un “Non-A-booleano”. Forse questo pragmatismo era fuori luogo, e la norma dovrebbe aver richiesto isnan( ), ma che avrebbe reso NaN quasi impossibile da usare in modo efficiente e conveniente per diversi anni, mentre il mondo aspettava l'adozione linguaggio di programmazione. Non sono convinto che sarebbe stato un compromesso ragionevole.

Per essere franchi: il risultato di NaN == NaN non ha intenzione di cambiare adesso. Meglio imparare a convivere con essa piuttosto che lamentarsi su internet. Se si vuole sostenere che una relazione d'ordine adatta Per i contenitori anche esiste, mi sento di raccomandare sostenendo che il vostro linguaggio di programmazione preferito implementare il predicato totalOrder standardizzato in IEEE-754 (2008). Il fatto che non ha già parla alla validità della preoccupazione di Kahan che ha motivato l'attuale stato di cose.

Altri suggerimenti

NaN può essere pensato come un indefinito stato / numero. simile al concetto di 0/0 essendo definita o sqrt (-3) (nel sistema dei numeri reali in cui la vita virgola mobile).

NaN viene utilizzato come una sorta di segnaposto per questo stato indefinito. Matematicamente parlando, indefinita non è uguale a indefinito. Né si può dire un valore indefinito è maggiore o minore di un altro valore indefinito. Pertanto tutti i confronti restituiscono falso.

Questo comportamento è vantaggioso anche nei casi in cui si confronta sqrt (-3) a sqrt (-2). Avrebbero entrambi tornano NaN ma non sono equivalenti, anche se restituiscono lo stesso valore. Pertanto avendo l'uguaglianza di tornare sempre falsa quando si tratta di Nan è il comportamento desiderato.

Per gettare ancora un'altra analogia. Se io ti consegno due scatole, e vi dico che nessuno di essi contiene una mela, vorresti dirmi che le scatole contengono la stessa cosa?

NaN non contiene informazioni su ciò che qualcosa è, proprio quello che non lo è. Quindi questi elementi non possono mai sicuramente dire di essere uguale.

alle voci di Wikipedia su NaN , le seguenti pratiche possono causare NaNs:

  • Tutte le operazioni matematiche> con NaN come almeno un operando
  • Le divisioni 0/0, ∞ / ∞, ∞ / -∞, -∞ / ∞, e -∞ / -∞
  • Le moltiplicazioni 0 × ∞ e 0 × -∞
  • Le aggiunte ∞ + (-∞), (-∞) + ∞ e sottrazioni equivalenti.
  • applicando una funzione ad argomenti fuori del suo dominio, tra la radice quadrata di un numero negativo, prendendo il logaritmo di un numero negativo, prendendo la tangente di un multiplo dispari di 90 gradi (o n / 2 radianti), o prendendo il seno inverso o coseno di un numero che è inferiore a -1 o superiore a 1.

Dato che non c'è modo di sapere quale di queste operazioni ha creato la NaN, non c'è modo per confrontarli che abbia un senso.

Non so la logica di progettazione, ma ecco un estratto dallo standard IEEE 754-1985:

.

"Sarà possibile confrontare numeri a virgola mobile in tutti i formati, anche se formati gli operandi differiscono confronti sono esatte e mai di overflow, né underflow Quattro relazioni reciprocamente esclusivi sono possibili:. Minore, uguale, maggiore di , e non ordinata. L'ultimo caso si verifica quando almeno uno degli operandi è NaN. Ogni NaN raffronta non ordinata con tutto, compreso se stesso ".

Sembra strano solo perché la maggior parte degli ambienti di programmazione che permettono NaNs non consentono anche la logica a 3 valori. Se gettate a 3 valori logica nel mix, diventa coerente:

  • (2.7 == 2.7) = true
  • (2,7 == 2.6) = false
  • (2,7 == NaN) = sconosciuto
  • (NaN == NaN) = sconosciuto

Anche .NET non fornisce un operatore di bool? operator==(double v1, double v2), quindi si è ancora bloccato con il risultato (NaN == NaN) = false sciocca.

Sto indovinando che NaN (non un numero) significa esattamente questo:. Questo non è un numero e confrontando quindi in realtà non ha senso

E 'un po' come l'aritmetica in SQL con operandi null: Tutti risultato null

.

I confronti per i numeri in virgola mobile confrontano i valori numerici. Pertanto, essi non possono essere utilizzati per i valori non numerici. NaN pertanto non può essere paragonata in senso numerico.

La risposta semplicistica è che un NaN non ha alcun valore numerico, quindi non c'è nulla in essa da confrontare con qualsiasi altra cosa.

Si potrebbe considerare di prova per la sostituzione e le vostre NaNs con + INF se si desidera loro di agire come + INF.

Nan è una nuova istanza implicita (di un tipo speciale di Runtime Error). Ciò significa NaN !== NaN per la stessa ragione che new Error !== new Error;

E tenere a mente come implicito è anche visto gli errori di fuori, ad esempio nel contesto delle espressioni regolari significa /a/ !== /a/ che è lo zucchero appena sintassi per new RegExp('a') !== new RegExp('a')

Mentre sono d'accordo che i confronti di Nan con qualsiasi numero reale dovrebbe essere ordinata, penso che c'è giusta causa per il confronto NaN con se stesso. Come, ad esempio, si fa a scoprire la differenza tra NaN di segnalazione e NaN tranquille? Se pensiamo i segnali come un insieme di valori booleani (cioè un vettore di bit) si potrebbe anche chiedere se i bit-vettori sono uguali o diversi e ordinare i set di conseguenza. Ad esempio, sulla decodificazione un massimo esponente polarizzato, se significando sono stati lasciati spostata in modo da allineare il bit più significativo del significando il bit più significativo del formato binario, un valore negativo potrebbe essere un tranquillo NaN e qualsiasi valore positivo sarebbe un NaN di segnalazione. Zero del corso è riservato per l'infinito e il confronto sarebbe non ordinata. allineamento MSB consentirebbe il confronto diretto dei segnali anche da diversi formati binari. Due NaN con lo stesso insieme di segnali sarebbe pertanto equivalente e dare significato alla parità.

Perché la matematica è il campo in cui "solo esistono" numeri. Nel calcolare voi deve di inizializzazione quei numeri e mantenere il loro stato in base alle proprie esigenze. A quei tempi l'inizializzazione della memoria ha funzionato nei modi si potrebbe mai fare affidamento su. Non hai mai potrebbe consentire a voi stessi di pensare a questo "Oh, sarebbe essere inizializzato con 0xCD tutto il tempo, il mio algo non sarà rotto" .

Quindi è necessario corretta non-miscelazione solvente che è abbastanza appiccicoso di non non lasciare che il vostro algoritmo risucchiati in e rotti. Buoni algoritmi che coinvolgono numeri sono per lo più andare a lavorare con i rapporti, e quelli se () Relazioni verrà omessa.

Questa è solo grasso che si può mettere in nuova variabile al momento della creazione, invece di programmare inferno caso dalla memoria del computer. E il vostro algoritmo di qualunque esso sia, non si romperà.

In seguito, quando si ancora improvvisamente scoprire che l'algoritmo sta producendo NaNs, è possibile pulirla, esaminando ogni ramo una alla volta. Anche in questo caso, "sempre falsa" regola sta aiutando molto in questo.

Per quanto mi riguarda, il modo più semplice per spiegare che è:

  

ho qualcosa e se non è una mela allora è un'arancia?

Non si può confrontare NaN con qualcos'altro (anche se stesso), perché non ha un valore. Inoltre può essere qualsiasi valore (ad eccezione di un numero).

  

ho qualcosa e se non è uguale a un numero allora è una stringa?

risposta molto breve:

Perché il seguente: nan / nan = 1 NON devono essere in possesso. In caso contrario, sarebbe inf/inf 1.

(Pertanto nan non può essere uguale a nan. Quanto > o <, se nan rispetterà qualsiasi relazione d'ordine in un insieme che soddisfa la proprietà di Archimede, avremmo nuovamente nan / nan = 1 al limite).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top