2015-03-04 9 views
7

Il seguente programma (adattato da here) sta dando risultati incoerenti quando compilato con GCC (4.8.2) e Clang (3.5.1). In particolare, il risultato GCC non cambia anche se lo fa FLT_EVAL_METHOD.Same FLT_EVAL_METHOD, risultati diversi in GCC/Clang

#include <stdio.h> 
#include <float.h> 

int r1; 
double ten = 10.0; 

int main(int c, char **v) { 
    printf("FLT_EVAL_METHOD = %d\n", FLT_EVAL_METHOD); 
    r1 = 0.1 == (1.0/ten); 
    printf("0.1 = %a, 1.0/ten = %a\n", 0.1, 1.0/ten); 
    printf("r1=%d\n", r1); 
} 

Test:

$ gcc -std=c99 t.c && ./a.out 
FLT_EVAL_METHOD = 0 
0.1 = 0x1.999999999999ap-4, 1.0/ten = 0x1.999999999999ap-4 
r1=1 

$ gcc -std=c99 -mpfmath=387 t.c && ./a.out 
FLT_EVAL_METHOD = 2 
0.1 = 0x0.0000000000001p-1022, 1.0/ten = 0x0p+0 
r1=1 

$ clang -std=c99 t.c && ./a.out 
FLT_EVAL_METHOD = 0 
0.1 = 0x1.999999999999ap-4, 1.0/ten = 0x1.999999999999ap-4 
r1=1 

$ clang -std=c99 -mfpmath=387 -mno-sse t.c && ./a.out 
FLT_EVAL_METHOD = 2 
0.1 = 0x0.07fff00000001p-1022, 1.0/ten = 0x0p+0 
r1=0 

noti che, secondo this blog post, GCC 4.4.3 utilizzato all'uscita 0 anziché 1 nella seconda prova.

A possibly related question indica che un bug è stato corretto in GCC 4.6, il che potrebbe spiegare perché il risultato di GCC è diverso.

Vorrei confermare se uno di questi risultati sarebbe errato o se alcune sottili fasi di valutazione (ad esempio una nuova ottimizzazione del preprocessore) giustificherebbero la differenza tra questi compilatori.

+2

Ho dovuto indagare recentemente sul comportamento delle vecchie versioni di GCC e qualcuno mi ha indirizzato a https://gcc.godbolt.org che è stato molto utile. Non ha GCC 4.4.3, ma ha 4.4.7. –

risposta

6

Questa risposta è qualcosa che si dovrebbe risolvere prima di andare oltre, perché sta andando a fare un ragionamento su ciò che accade molto più difficile altrimenti:

Sicuramente stampando 0.1 = 0x0.07fff00000001p-1022 o 0.1 = 0x0.0000000000001p-1022 può essere solo un bug sulla tua piattaforma compilation causato dalla mancata corrispondenza di ABI quando si utilizza -mfpmath=387. Nessuno di questi valori può essere giustificato dall'eccesso di precisione.

Si potrebbe provare a includere il proprio formato di conversione in formato leggibile nel file di test, in modo che tale conversione sia anche compilata con -mfpmath=387. O fare un piccolo stub in un altro file, non è compilato con questa opzione, con un convegno minimalista chiamata:

In un altro file:

double d; 
void print_double(void) 
{ 
    printf("%a", d); 
} 

Nel file compilato con -mfpmath=387:

extern double d; 
d = 0.1; 
print_double(); 
+0

Infatti, compilando prima il file 'print_double' senza' -mfpmath = 387', e quindi collegando il suo '.o' all'altro file risolve il problema: stampa' 0x1.99999999999999apap-4' in entrambi i compilatori, con e senza il flag '-mfpmath = 387'. – anol

+0

Ho modificato la risposta per renderla più leggibile/ – CuriosGuy

+0

@CuriosGuy Nota che lo stile che hai usato, ottenuto con gli apici in linea '\' ', è inteso per ** codice **.OTOH non ci sono raccomandazioni per l'output del programma, quindi è probabilmente accettabile qui (ma non usare '\' 'per enfatizzare). –

0

Ignorando il problema printf che Pascal Cuoq ha affrontato, penso che GCC sia corretto qui: secondo lo standard C99, FLT_EVAL_METHOD == 2 dovrebbe

valutare tutte le operazioni e le costanti nell'intervallo e la precisione del tipo long double.

Quindi, in questo caso, sia 0.1 e 1.0/ten vengono valutati per una precisione approssimazione estesa di 1/10.

Non sono sicuro di cosa stia facendo Clang, anche se this question potrebbe fornire un aiuto.