Domanda

Qual è il modo migliore di usare NaN in C ++?

Ho trovato std::numeric_limits<double>::quiet_NaN() e std::numeric_limits<double>::signaling_NaN(). Vorrei usare signaling_NaN per rappresentare una variabile non inizializzata come segue:

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

Questo, tuttavia, segnala (solleva un'eccezione) sull'assegnazione. Voglio che sollevi un'eccezione sull'uso, non sull'assegnazione.

Esiste un modo per utilizzare <=> senza sollevare un'eccezione sull'assegnazione? Esiste una valida alternativa portatile a <=> che genererà un'eccezione in virgola mobile quando utilizzata?

È stato utile?

Soluzione

Dopo aver esaminato un po 'di più, sembra che signaling_NaN sia inutile come previsto. Se le eccezioni in virgola mobile sono abilitate, la sua chiamata conta come elaborazione di un NaN di segnalazione, quindi genera immediatamente un'eccezione. Se le eccezioni in virgola mobile sono disabilitate, l'elaborazione di un NaN di segnalazione lo riduce automaticamente in un NaN silenzioso, quindi <=> non funziona in nessun modo.

Codice di Menkboy funziona, ma il tentativo di utilizzare la segnalazione di NaN si imbatte in altri problemi: non esiste un modo portatile per abilitare o disabilitare le eccezioni in virgola mobile (come accennato a qui e qui ) e se fai affidamento sull'eccezione dell'abilitazione , il codice di terze parti potrebbe disabilitarli (come descritto qui ).

Quindi sembra che La soluzione di Motti è davvero la scelta migliore.

Altri suggerimenti

Ciò che significa NAN di segnalazione è che quando la CPU lo incontra viene emesso un segnale (da cui il nome). Se si desidera rilevare variabili non inizializzate, l'innalzamento del livello di avviso sul compilatore in genere rileva tutti i percorsi che utilizzano valori non inizializzati. Se non riesci a utilizzare una classe wrapper che memorizza un booleano che dice se il valore è inizializzato:

template <class T>
class initialized {
    T t;
    bool is_initialized;
public:
    initialized() : t(T()), is_initialized(false) { }
    initialized(const T& tt) : t(tt), is_initialized(true) { }
    T& operator=(const T& tt) { t = tt; is_initialized = true; return t; }
    operator T&() {
         if (!is_initialized)
             throw std::exception("uninitialized");
         return t; 
   }
};

Puoi scrivere un NaN di segnalazione in una variabile senza innescare un'eccezione con qualcosa del genere (nb: non testato)

void set_snan( double &d )
{
    long long *bits = (long long *)&d;
    *bits = 0x7ff0000080000001LL;
}

Funzionerà nella maggior parte dei posti, ma no, non è portatile al 100%.

Bene, curando la definizione di NaN sia di segnalazione che di segnalazione, non riesco davvero a distinguere alcuna differenza.

Potresti usare tu stesso il codice usato in quelle funzioni, forse impedisce un'eccezione in quel modo, ma non vedendo alcuna eccezione in quelle due funzioni, penso che potrebbe essere correlato a qualcos'altro.

Se si desidera assegnare direttamente il NaN:

double value = _Nan._Double;

Risposta semplice: Fai qualcosa del genere nel file di intestazione e usalo ovunque:

#define NegativeNaN log(-1)

Se desideri fare qualche tipo di manipolazione su di essi, scrivi meglio alcune funzioni di wrapper esteso intorno a exp() come extended_exp() e così via!

L'implementazione di C ++ potrebbe avere un'API per accedere all'ambiente in virgola mobile per verificare e cancellare determinate eccezioni in virgola mobile. Vedi la mia risposta a una domanda correlata per ulteriori informazioni.

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