2015-03-04 20 views
7

Poniższy program (zaadaptowany z here) daje niespójne wyniki w kompilacji z GCC (4.8.2) i Clangiem (3.5.1). W szczególności wynik GCC nie zmienia się, nawet gdy ma on wartość FLT_EVAL_METHOD.To samo FLT_EVAL_METHOD, różne wyniki w 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); 
} 

Testy:

$ 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 

Należy zauważyć, że zgodnie z this blog post GCC 4.4.3 stosowane do wyjścia 0 zamiast 1 w drugim badaniu.

A possibly related question wskazuje, że błąd został poprawiony w GCC 4.6, co może wyjaśniać, dlaczego wynik GCC jest inny.

Chciałbym potwierdzić, czy którykolwiek z tych wyników byłby niepoprawny, lub gdyby niektóre subtelne kroki oceny (np. Nowa optymalizacja preprocesora) uzasadniałyby różnicę między tymi kompilatorami.

+2

Musiałem ostatnio zbadać zachowanie starych wersji GCC i ktoś wskazał mi na https://gcc.godbolt.org, który był bardzo przydatny. Nie ma GCC 4.4.3, ale ma 4.4.7. –

Odpowiedz

6

Ta odpowiedź jest o czymś, które należy rozwiązać przed wyjazdem dalej, ponieważ ma zamiar zrobić rozumowanie o tym, co dzieje się dużo trudniejsze inaczej:

pewnością drukowania 0.1 = 0x0.07fff00000001p-1022 lub 0.1 = 0x0.0000000000001p-1022 może być tylko błąd na platformie kompilacji spowodowane niedopasowaniem ABI podczas używania -mfpmath=387. Żadnej z tych wartości nie można usprawiedliwić nadmierną precyzją.

Możesz spróbować włączyć własny plik konwersji do odczytu w pliku testowym, aby ta konwersja została również skompilowana z -mfpmath=387. Lub zrobić mały niedopałek w innym pliku, nie skompilowany z tej opcji, o minimalistycznej konwencji rozmowy:

Innymi pliku:

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

w pliku skompilowanego z -mfpmath=387:

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

W rzeczywistości kompilowanie najpierw pliku 'print_double' bez' -mfpmath = 387', a następnie łączenie jego '.o' z drugim plikiem rozwiązuje problem: drukuje' 0x1.999999999999ap-4' w obu kompilatorach, zi bez flaga '-mfpmath = 387'. – anol

+0

Edytowałem odpowiedź, aby uczynić ją bardziej czytelną/ – CuriosGuy

+0

@ CuriosGuy Zwróć uwagę, że styl, którego używałeś, uzyskany z inkrementem backlitów '' ', jest przeznaczony dla ** kodu **.OTOH nie ma zaleceń dotyczących wyjścia programu, więc jest to prawdopodobnie do przyjęcia tutaj (ale nie używaj '\' 'dla podkreślenia). –

0

Ignorując problem z adresem printf adresowany przez Pascal Cuoq, myślę, że GCC jest tutaj poprawny: zgodnie ze standardem C99, FLT_EVAL_METHOD == 2 powinien być

ocenia wszystkie operacje i stałe w zakresie i precyzji typu long double.

Tak więc, w tym przypadku, zarówno 0.1 i 1.0/ten są oceniane na dłuższy precyzyjnego zbliżenia 1/10.

Nie jestem pewien, co robi Clang, ale this question może zapewnić pomoc.

Powiązane problemy