2010-08-29 17 views
28

Durante la scrittura di alcuni casi di test e alcuni test verificano il risultato di un NaN.Negativo NaN non è un NaN?

Ho provato ad utilizzare std::isnan ma i failes asserzione:

Assertion `std::isnan(x)' failed. 

Dopo aver stampato il valore di x, si è scoperto che è negativo NaN (-nan) che è del tutto accettabile nel mio caso.

Dopo aver tentato di utilizzare il fatto che NaN != NaN e utilizzando assert(x == x), il compilatore mi fa un "favore" e ottimizza l'assert.

Anche la funzione isNaN viene ottimizzata.

Come è possibile verificare l'uguaglianza di NaN e -NaN?

+7

o_O? NaN negativo * è * NaN. – kennytm

+1

Puoi mostrare come scrivi il tuo 'isNaN', e forse quello incorporato dal tuo compilatore se ce l'hai? Un modo per testare ** a ** NaN (ce ne sono diversi, come hai notato) è testare il pattern di bit su http://en.wikipedia.org/wiki/NaN (l'esponente è 11..11). –

+0

Quale compilatore stai usando e come si presenta il tuo codice di prova? – jalf

risposta

31

Questo è imbarazzante.

Il motivo per il compilatore (GCC in questo caso) è stata l'ottimizzazione via il confronto e isnan restituito false era perché qualcuno nella mia squadra aveva acceso -ffast-math.

Dalla documentazione:

 
-ffast-math 
    Sets -fno-math-errno, -funsafe-math-optimizations, 
    -fno-trapping-math, -ffinite-math-only, -fno-rounding-math, -fno-signaling-nans and fcx-limited-range. 

    This option causes the preprocessor macro __FAST_MATH__ to be defined. 

    This option should never be turned on by any -O option since it can result in incorrect output for programs which depend on an exact implementation of IEEE or ISO rules/specifications for math functions. 

Avviso la frase finale - -ffast-math è a rischio.

+0

Ho appena notato lo stesso identico problema con il compilatore MS quando usato con/fp: flag veloce. Funziona bene con/fp: preciso però. – stijn

0

C'è C99 isnan() che dovresti essere in grado di utilizzare.

Se nella tua implementazione non funziona correttamente (quale è quello?) Puoi implementare il tuo, reinterpretando_casting a long e facendo magie IEEE.

+2

Questo 'isnan' è esattamente ciò che sta già utilizzando. –

-3

Si basa sull'articolo di wikipedia pubblicato nei commenti. Nota che è completamente non testato: dovrebbe darti un'idea di qualcosa che puoi fare comunque.

bool reallyIsNan(float x) 
{ 
    //Assumes sizeof(float) == sizeof(int) 
    int intIzedX = *(reinterpret_cast<int *>(&x)); 
    int clearAllNonNanBits = intIzedX & 0x7F800000; 
    return clearAllNonNanBits == 0x7F800000; 
} 

EDIT: Penso davvero che dovresti prendere in considerazione la possibilità di presentare un bug ai ragazzi di GLIBC su quello.

+0

Ciò restituirà 'true' anche per' Inf'. –

+0

@ Sam: come ho detto, non l'ho testato, l'ho preso da Wikipedia. Non ho mai avanzato alcuna pretesa sulla sua correttezza. In ogni caso, l'OP ha già risposto a questa domanda. Non vedo perché stiamo scavando una domanda vecchia di 1,5 anni che è già stata contrassegnata come risposta e lamentandoci. –

+4

Non mi lamento; sei. Ho trovato questa domanda da una ricerca su Google e ho visto che non aveva una risposta soddisfacente. Sto commentando le risposte errate o incomplete perché non voglio che qualcun altro passi il tempo a capire perché sono errate o incomplete. Inoltre, la risposta approvata fornisce una spiegazione, non una soluzione. Quanti anni ha questa domanda non ha assolutamente alcuna rilevanza. Se hai un problema con questo, probabilmente hai perso il punto di questo sito. Rilassati, amico. –

0

È possibile controllare i bit del numero. IEEE 754 ha definito maschera per NaN:

  • La segnalazione NaN è rappresentata da qualsiasi sequenza di bit tra X'7F80 0001' e X'7FBF FFFF 'o tra X'FF80 0001' e X'FFBF FFFF'.
  • Un NaN silenzioso è rappresentato da qualsiasi schema di bit tra X'7FC0 0000 'e X'7FFF FFFF' o tra X'FFC0 0000 'e X'FFFF FFFF'.

Questo potrebbe non essere portatile, ma se si è certi del proprio platfofm può essere accettabile. Altro: http://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/index.jsp?topic=/com.ibm.xlf101l.doc/xlfopg/fpieee.htm

+0

Questa sarebbe stata la mia ultima risorsa, sto ancora cercando di trovare la soluzione più portabile. – LiraNuna

+0

@LiraNuna non è così drammatico. Se lo implementate tenendo a mente gli endiannes, sarà abbastanza semplice da usare – Andrey

1

Questo mi sembra un errore nell'implementazione di isnan() della libreria. Funziona bene su gcc 4.2.1 su Snow Leopard. Tuttavia, che ne dici di provare questo?

std::isnan(std::abs(yourNanVariable)); 

Ovviamente, non posso provarlo, dal momento che è std::isnan(-NaN)true sul mio sistema.

EDIT: Con -ffast-math, a prescindere dal commutatore -O, 4.2.1 gcc su Snow Leopard pensa che NAN == NAN è true, come è NAN == -NAN. Questo potrebbe potenzialmente rompere il codice in modo catastrofico. Suggerirei di lasciare -ffast-math o almeno testare per risultati identici nei build usando e non usandolo ...

3

isnan() si prevede che abbia un comportamento non definito con -ffast-math.

Questo è quello che uso nella mia suite di test:

#if defined __FAST_MATH__ 
# undef isnan 
#endif 
#if !defined isnan 
# define isnan isnan 
# include <stdint.h> 
static inline int isnan(float f) 
{ 
    union { float f; uint32_t x; } u = { f }; 
    return (u.x << 1) > 0xff000000u; 
} 
#endif