2013-04-03 11 views
8

IEEE754 richiede che i NaN non siano ordinati; minore di, maggiore di, uguale ecc. dovrebbe restituire tutti falso quando uno o entrambi gli operandi sono NaN.Le ottimizzazioni VC++ interrompono il confronto con NaN?

L'esempio riportato di seguito si ottiene la corretta F F F F F T come previsto quando compilato utilizzando g ++ a tutti i livelli di ottimizzazione, e quando compilato utilizzando cl.exe (versione a 32 bit 15.00.30729.01) VC++ s 'senza argomenti di ottimizzazione o qualsiasi combinazione di/od,/fp: fast,/arch: SSE.

Tuttavia, quando compilato con/O1 o/O2 (e qualsiasi/nessun altro argomento di ottimizzazione), i risultati T T F F F T, anche con/Op anche specificato.

La versione a 64 bit di cl.exe produce molte varianti - T T F F F T, T T T F F T, T T T F F F etc - a seconda del livello di ottimizzazione e se/fp: veloce è specificato, ma come con la versione a 32 bit, un comportamento compatibile sembra solo per essere possibile con tutte le ottimizzazioni disabilitate.

Sto facendo qualche errore evidente? C'è un modo per far sì che il compilatore rispetti gli standard qui senza sacrificare tutte le altre ottimizzazioni?

#include <limits> 
#include <stdio.h> 

int main(int argc, char const ** argv) 
{ 
    float test = std::numeric_limits<float>::quiet_NaN(); 

    printf("%c %c %c %c %c %c\n", 
      (test < test) ? 'T' : 'F', 
      (test <= test) ? 'T' : 'F', 
      (test == test) ? 'T' : 'F', 
      (test > test) ? 'T' : 'F', 
      (test >= test) ? 'T' : 'F', 
      (test != test) ? 'T' : 'F' 
     ); 

    return 0; 
} 

Un esempio build.cmd che riproduce il problema:

set "PATH=c:\program files (x86)\Microsoft Visual Studio 9.0\VC\bin\amd64;c:\program files (x86)\Microsoft Visual Studio 9.0\Common7;c:\program files (x86)\Microsoft Visual Studio 9.0\Common7\IDE" 
set "LIB=c:\program files (x86)\microsoft visual studio 9.0\vc\lib\x64;c:\program files\Microsoft SDKs\Windows\v6.0A\Lib\x64" 
cl test.cpp /fp:fast /Od /c /I "c:\program files (x86)\microsoft visual studio 9.0\vc\include" 
link "/LIBPATH:C:/Program Files (x86)/Microsoft Visual Studio 9.0/vc/lib/amd64" "/LIBPATH:C:\Program Files\Microsoft SDKs\Windows\v6.0A\/Lib/x64" /DEBUG /IGNORE:4199 /IGNORE:4221 /MACHINE:X64 /SUBSYSTEM:CONSOLE test.obj 
test 

EDIT

Per la cronaca, l'esempio originariamente riportati nella domanda utilizzato

inline float QNaN() 
{ 
    static int const QNaNValue = 0x7fc00000; 
    return *(reinterpret_cast<float const*>(&QNaNValue)); 
} 

generare un NaN; come un numero di commenti e risposte sottolineano, questo è un comportamento indefinito, e la sua sostituzione con std :: numeric_limits :: quiet_NaN() ha effettivamente risolto il problema per alcune versioni del CL.exe a 32 bit

+1

Sospetto che possa avere qualcosa a che fare con il comportamento indefinito causato dal trattare un oggetto 'int' come' float'. Hai provato '0.0/0.0' invece? –

+3

La funzione QNaN ha un comportamento non definito. Hai provato [usando le strutture C++] (http://en.cppreference.com/w/cpp/types/numeric_limits/quiet_NaN) per ottenere invece un NaN? –

+0

Questa è una falsa pista, temo (il problema originale che ho incontrato era piuttosto complesso di questo semplice esempio l'ho ristretto a). Ho modificato l'esempio per utilizzare la funzione di libreria standard suggerita nei commenti, i risultati sono invariati. – moonshadow

risposta

4

Sei invocando un comportamento indefinito lanciando uno int* a float*. Ho provato il tuo codice con VS 2010, usando std::numeric_limits<float>::quiet_NaN() invece del cast, e ha dato il risultato previsto (tutti tranne l'ultimo erano false) con /O2 e /fp:fast.

UPDATE

ho copia-incollato il vostro esempio rivisto in entrambi VS 2010 e VS 2005. In entrambi questi, il compilatore a 32 bit produce risultati corretti (F F F F F T), mentre il 64-bit il compilatore no.

+0

Hm. Quale versione del compilatore? Allego un esempio di comandi di compilazione che riproduce il comportamento errato per me con std :: numeric_limits :: quiet_NaN() – moonshadow

+1

Hm. Utilizzando la funzione di libreria, * e * omitting/fp: fast, si ottiene il comportamento corretto per tutti i casi (build a 32 bit e 64 bit, tutti i livelli di ottimizzazione) qui. Vado con quello :) – moonshadow

+2

@moonshadow perché non lo postare come risposta invece di accettarne uno che non risolve il problema? –

4

Questo perché la funzione QNaN richiama UB violando il rigoroso aliasing. Il compilatore VS ha tutti i diritti per produrre qualsiasi comportamento.

0

Credo che stiate cercando l'opzione /fp:strict.

Si consiglia inoltre la float_control pragma.

tutto questo era proprio nella documentazione in "Opzioni di compilatore per la categoria"

+0

Il problema è stato risolto in precedenza, ma per la cronaca, nessuno di '/ fp: strict','/fp: precise', o uno qualsiasi dei pragmi di aiuto qui. – moonshadow

+0

@moonshadow, non è chiaro dal commento precedente: "Ho modificato l'esempio per utilizzare la funzione di libreria standard suggerita nei commenti, i risultati sono invariati". Quindi sembra che il problema non sia risolto. Hai provato '/ fp: strict' con la tua precedente funzione' QNan() '? Sto solo cercando di chiarire. – Ben

5

Quindi, per riassumere, ci sono stati un certo numero di problemi separati:

  • il codice di esempio originale ha restituito un comportamento indefinito violando il rigoroso aliasing. Risolvere ciò era sufficiente per risolvere il problema per alcune versioni del compilatore a 32 bit.

  • con quella Risolto il problema, rimuovere /fp:fast risolto il problema per tutte le versioni di entrambi i compilatori a 32-bit e 64-bit a mia disposizione

  • Martinho menziona il fatto che il problema non esiste più in cl 16,0 anche con /fp:fast