Sono stato membro del comitato IEEE-754, cercherò di aiutare a chiarire un po 'le cose.
Prima di tutto, i numeri in virgola mobile non sono numeri reali e l'aritmetica in virgola mobile non soddisfa gli assiomi dell'aritmetica reale. La tricotomia non è l'unica proprietà dell'aritmetica reale che non vale per i galleggianti, né la più importante. Per esempio:
- L'aggiunta non è associativa.
- La legge distributiva non vale.
- Esistono numeri in virgola mobile senza inversioni.
Potrei andare avanti. Non è possibile specificare un tipo aritmetico di dimensioni fisse che soddisfi tutte le proprietà dell'aritmetica reale che conosciamo e amiamo. Il comitato 754 deve decidere di piegare o rompere alcuni di essi. Questo è guidato da alcuni principi piuttosto semplici:
- Quando possiamo, abbiniamo il comportamento dell'aritmetica reale.
- Quando non possiamo, cerchiamo di rendere le violazioni il più prevedibili e facili da diagnosticare.
Per quanto riguarda il tuo commento "ciò non significa che la risposta corretta sia falsa", questo è sbagliato. Il predicato (y < x)
chiede se y
è inferiore a x
. Se y
è NaN, allora è non meno di qualsiasi valore a virgola mobile x
, quindi la risposta è necessariamente falso.
Ho detto che la tricotomia non vale per i valori in virgola mobile. Tuttavia, esiste una proprietà simile che detiene. Clausola 5.11, paragrafo 2 della norma 754-2008:
Sono possibili quattro relazioni reciprocamente esclusive: minore di, uguale, maggiore di e non ordinata. L'ultimo caso si presenta quando almeno un operando è NaN. Ogni NaN si confronta non ordinata con tutto, incluso se stessa.
Per quanto riguarda la scrittura di codice aggiuntivo per gestire i NaN, di solito è possibile (anche se non sempre facile) strutturare il codice in modo tale che i NaN cadano correttamente, ma non è sempre così. In caso contrario, potrebbe essere necessario un codice aggiuntivo, ma questo è un piccolo prezzo da pagare per la comodità che la chiusura algebrica ha portato all'aritmetica in virgola mobile.
Addendum: molti commentatori hanno sostenuto che sarebbe più utile preservare la riflessività dell'uguaglianza e della tricotomia sulla base del fatto che l'adozione di NaN! = NaN non sembra preservare alcun assioma familiare. Confesso di avere una certa simpatia per questo punto di vista, quindi ho pensato di rivisitare questa risposta e fornire un po 'più contesto.
La mia comprensione dal parlare con Kahan è che NaN! = NaN ha avuto origine da due considerazioni pragmatiche:
Ciò x == y
dovrebbe essere equivalente x - y == 0
ogni volta che è possibile (oltre ad essere un teorema della vera aritmetica, questo rende l'implementazione hardware del confronto più efficiente in termini di spazio, che era della massima importanza al momento dello sviluppo dello standard; nota, tuttavia, che questo è violato per x = y = infinito, quindi non è un grande motivo da solo; avrebbe potuto ragionevolmente essere piegato (x - y == 0) or (x and y are both NaN)
).
Ancora più importante, non c'era isnan( )
predicato al momento in cui NaN fu formalizzata nell'aritmetica dell'8087; era necessario fornire ai programmatori un mezzo conveniente ed efficiente per rilevare i valori di NaN che non dipendevano dai linguaggi di programmazione fornendo qualcosa di simile isnan( )
che potrebbe richiedere molti anni. Citerò gli stessi scritti di Kahan sull'argomento:
Se non ci fosse modo di sbarazzarsi di NaN, sarebbero inutili come gli indefiniti su CRAY; non appena ne si incontrava uno, il calcolo sarebbe meglio fermato piuttosto che continuare per un tempo indefinito fino a una conclusione indefinita. Ecco perché alcune operazioni su NaN devono fornire risultati non NaN. Quali operazioni? … Le eccezioni sono i predicati C “x == x” e “x! = X”, che sono rispettivamente 1 e 0 per ogni numero infinito o finito x ma invertiti se x non è un numero (NaN); questi forniscono l'unica semplice distinzione ineccepibile tra NaN e numeri in lingue che non hanno una parola per NaN e un predicato IsNaN (x).
Si noti che questa è anche la logica che esclude la restituzione di qualcosa di simile a un "non-booleano". Forse questo pragmatismo era fuori luogo e lo standard avrebbe dovuto essere richiesto isnan( )
, ma ciò avrebbe reso NaN quasi impossibile da usare in modo efficiente e conveniente per diversi anni mentre il mondo attendeva l'adozione del linguaggio di programmazione. Non sono convinto che sarebbe stato un compromesso ragionevole.
Per essere schietti: il risultato di NaN == NaN non cambierà ora. Meglio imparare a conviverci che a lamentarsi su Internet. Se vuoi sostenere che dovrebbe esistere anche una relazione d'ordine adatta ai container , ti consiglio di sostenere che il tuo linguaggio di programmazione preferito implementi il totalOrder
predicato standardizzato in IEEE-754 (2008). Il fatto che non abbia già parlato della validità della preoccupazione di Kahan che ha motivato la situazione attuale.
while (fabs(x - oldX) > threshold)
, uscendo dal loop se si verifica la convergenza o se un NaN entra nel calcolo. Il rilevamento della NaN e il rimedio appropriato avverrebbero quindi al di fuori del ciclo.