Domanda

Recentemente ho letto parecchio su IEEE 754 e sull'architettura x87.Stavo pensando di utilizzare NaN come "valore mancante" in un codice di calcolo numerico su cui sto lavorando e speravo che l'utilizzo segnalazione NAN mi permetterebbe di catturare un'eccezione in virgola mobile nei casi in cui non voglio procedere con "valori mancanti". Al contrario, lo userei Tranquillo NaN per consentire al "valore mancante" di propagarsi attraverso un calcolo.Tuttavia, i NaN di segnalazione non funzionano come pensavo sulla base della documentazione (molto limitata) esistente su di essi.

Ecco un riepilogo di ciò che so (tutto questo utilizzando x87 e VC++):

  • _EM_INVALID (l'eccezione "non valida" IEEE) controlla il comportamento di x87 quando incontra NaN
  • Se _EM_INVALID è mascherato (l'eccezione è disabilitata), non viene generata alcuna eccezione e le operazioni possono restituire NaN silenzioso.Un'operazione che coinvolge la segnalazione di NaN lo farà non causerà la generazione di un'eccezione, ma verrà convertita in NaN silenzioso.
  • Se _EM_INVALID non è mascherato (eccezione abilitata), un'operazione non valida (ad esempio sqrt(-1)) provoca la generazione di un'eccezione non valida.
  • L'x87 Mai genera la segnalazione NaN.
  • Se _EM_INVALID viene smascherato, Qualunque l'uso di un NaN di segnalazione (anche inizializzando una variabile con esso) provoca la generazione di un'eccezione non valida.

La libreria standard fornisce un modo per accedere ai valori NaN:

std::numeric_limits<double>::signaling_NaN();

E

std::numeric_limits<double>::quiet_NaN();

Il problema è che non vedo alcuna utilità per la segnalazione NaN.Se _EM_INVALID è mascherato si comporta esattamente come NaN silenzioso.Poiché nessun NaN è paragonabile a nessun altro NaN, non esiste alcuna differenza logica.

Se _EM_INVALID è non mascherato (l'eccezione è abilitata), non è nemmeno possibile inizializzare una variabile con una segnalazione NaN:double dVal = std::numeric_limits<double>::signaling_NaN(); perché questo genera un'eccezione (il valore NaN di segnalazione viene caricato in un registro x87 per memorizzarlo nell'indirizzo di memoria).

Potresti pensare quanto segue, come ho fatto io:

  1. Maschera _EM_INVALID.
  2. Inizializza la variabile con la segnalazione NaN.
  3. Smaschera_EM_INVALID.

Tuttavia, il passaggio 2 fa sì che il NaN di segnalazione venga convertito in un NaN silenzioso, quindi lo faranno i successivi usi non causare la generazione di eccezioni!Allora, che cavolo?!

C'è qualche utilità o scopo in un NaN di segnalazione?Capisco che uno degli intenti originali fosse quello di inizializzare la memoria con esso in modo da poter rilevare l'uso di un valore in virgola mobile unitalizzato.

Qualcuno può dirmi se mi manca qualcosa qui?


MODIFICARE:

Per illustrare ulteriormente ciò che speravo di fare, ecco un esempio:

Prendi in considerazione l'esecuzione di operazioni matematiche su un vettore di dati (doppi).Per alcune operazioni, voglio consentire al vettore di contenere un "valore mancante" (fai finta che corrisponda a una colonna del foglio di calcolo, ad esempio, in cui alcune celle non hanno un valore, ma la loro esistenza è significativa).Per alcune operazioni, lo faccio non vuoi consentire al vettore di contenere un "valore mancante". Forse voglio intraprendere un modo di azione diverso se è presente un "valore mancante" nel set - forse eseguendo un'operazione diversa (quindi questo non è uno stato non valido in cui essere).

Questo codice originale sarebbe simile a questo:

const double MISSING_VALUE = 1.3579246e123;
using std::vector;

vector<double> missingAllowed(1000000, MISSING_VALUE);
vector<double> missingNotAllowed(1000000, MISSING_VALUE);

// ... populate missingAllowed and missingNotAllowed with (user) data...

for (vector<double>::iterator it = missingAllowed.begin(); it != missingAllowed.end(); ++it) {
    if (*it != MISSING_VALUE) *it = sqrt(*it); // sqrt() could be any operation
}

for (vector<double>::iterator it = missingNotAllowed.begin(); it != missingNotAllowed.end(); ++it) {
    if (*it != MISSING_VALUE) *it = sqrt(*it);
    else *it = 0;
}

Si noti che è necessario eseguire il controllo del "valore mancante". ogni iterazione del ciclo.Anche se capisco nella maggior parte dei casi, il sqrt (o qualsiasi altra operazione matematica) probabilmente metterà in ombra questo controllo, ci sono casi in cui l'operazione è minima (forse solo un'aggiunta) e il controllo è costoso.Per non parlare del fatto che il "valore mancante" toglie dal gioco un valore di input legale e potrebbe causare bug se un calcolo arriva legittimamente a quel valore (per quanto improbabile possa essere).Inoltre, per essere tecnicamente corretti, i dati immessi dall'utente dovrebbero essere confrontati con tale valore e dovrebbe essere intrapresa una linea d'azione appropriata.Trovo questa soluzione inelegante e tutt'altro che ottimale dal punto di vista prestazionale.Questo è un codice critico per le prestazioni e sicuramente non possiamo permetterci il lusso di strutture dati parallele o oggetti di elementi dati di qualche tipo.

La versione NaN sarebbe simile a questa:

using std::vector;

vector<double> missingAllowed(1000000, std::numeric_limits<double>::quiet_NaN());
vector<double> missingNotAllowed(1000000, std::numeric_limits<double>::signaling_NaN());

// ... populate missingAllowed and missingNotAllowed with (user) data...

for (vector<double>::iterator it = missingAllowed.begin(); it != missingAllowed.end(); ++it) {
    *it = sqrt(*it); // if *it == QNaN then sqrt(*it) == QNaN
}

for (vector<double>::iterator it = missingNotAllowed.begin(); it != missingNotAllowed.end(); ++it) {
    try {
        *it = sqrt(*it);
    } catch (FPInvalidException&) { // assuming _seh_translator set up
        *it = 0;
    }
}

Ora il controllo esplicito è eliminato e le prestazioni dovrebbero essere migliorate.Penso che tutto funzionerebbe se potessi inizializzare il vettore senza toccare i registri della FPU...

Inoltre, immagino qualunque che si rispetti sqrt l'implementazione controlla NaN e restituisce NaN immediatamente.

È stato utile?

Soluzione

A quanto mi risulta, allo scopo di segnalare NaN è inizializzare strutture dati, ma, naturalmente runtime inizializzazione C corre il rischio di avere la NaN caricata in un registro galleggiante come parte di inizializzazione , innescando il segnale perché il compilatore non risulta che questo valore float deve essere copiato utilizzando un registro intero.

mi auguro che si poteva possibile inizializzare un valore static con un NaN di segnalazione, ma anche questo richiederebbe una gestione speciale dal compilatore per evitare di averlo trasformato in un NaN tranquilla. Si potrebbe forse usare un po 'di magia colata per evitare di averlo trattato come un valore float durante l'inizializzazione.

Se si stesse scrivendo in ASM, questo non sarebbe un problema. ma in C e in particolare in C ++, penso che si dovrà sovvertire il sistema di tipo al fine di inizializzare una variabile con NaN. Io suggerisco di usare memcpy.

Altri suggerimenti

L'utilizzo di valori speciali (anche NULL) può rendere i tuoi dati molto più confusi e il tuo codice molto più confuso.Sarebbe impossibile distinguere tra un risultato QNaN e un valore "speciale" QNaN.

Potrebbe essere meglio mantenere una struttura dati parallela per tenere traccia della validità, o forse avere i dati FP in una struttura dati diversa (sparsa) per mantenere solo dati validi.

Questo è un consiglio abbastanza generale;valori speciali sono molto utili in alcuni casi (ad es.memoria molto ristretta o vincoli di prestazioni), ma man mano che il contesto diventa più ampio possono causare più difficoltà di quanto valgano.

Non potreste semplicemente avere un uint64_t const nella quale i bit sono stati impostati a quelli di una segnalazione nan? fintanto che lo trattano come un tipo intero, il nan segnalazione non è diverso da altri numeri interi. Si potrebbe scrivere dove vuoi tramite puntatore-casting:

Const uint64_t sNan = 0xfff0000000000000;
Double[] myData;
...
Uint64* copier = (uint64_t*) &myData[index];
*copier=sNan | myErrorFlags;

Per info sui bit per impostare: https://www.doc.ic.ac.uk/ ~ eedwards / compsys / galleggiare / nan.html

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