2016-04-06 2 views
9

Beispielcodem32 und m64 Compiler-Optionen bietet verschiedene Ausgabe

#include "stdio.h" 
#include <stdint.h> 

int main() 
{ 
    double d1 = 210.01; 
    uint32_t m = 1000; 
    uint32_t v1 = (uint32_t) (d1 * m); 

    printf("%d",v1); 
    return 0; 
} 

Ausgang
1. Wenn mit -m32 Option kompiliert (dh gcc -g3 -m32 test.c)

/test 174 # ./a.out 
210009 

2 . Wenn Sie mit -m64 Option (dh gcc -g3 -m64 test.c)

test 176 # ./a.out 
210010 

kompilieren Warum erhalte ich eine abweichen en?
Mein Verständnis "war", m würde zu double hochgestuft werden und die Multiplikation würde nach unten auf unit32_t geworfen werden. Darüber hinaus, da wir eine Ganzzahl vom Typ stdint verwenden, würden wir die Mehrdeutigkeit in Bezug auf die Architektur usw. weiter entfernen.

Ich weiß, dass etwas hier fischig ist, aber nicht in der Lage ist, es festzuhalten.

Update:
Nur um (für einen der Kommentar) zu verdeutlichen, wird das obige Verhalten für beide gcc und g ++ gesehen.

+2

Wenn Sie gcc so nennen, es ist als C-Code kompiliert (was es zu sein scheint), nicht C++. Fügen Sie keine Tags für nicht verwandte Sprachen hinzu. – Olaf

+0

@Olaf: Ok. Ich werde die Frage aktualisieren. Es tritt sowohl für den gcc- als auch für den g ++ - Compiler auf. Es ist also nicht spezifisch für c/C++. Vielen Dank! –

+0

1) Mach das nicht zu einer C++ Frage! C und C++ sind ** verschiedene ** Sprachen. 1a) Sie sollten anderen Code in C++ mit Iostreams und C++ Cast-Operatoren verwenden. 2) Sie verwenden den falschen Typ-Spezifizierer in der Formatzeichenfolge. Verwenden Sie 'inttypes.h' Makros, um eine Ganzzahl mit fester Breite zu drucken. – Olaf

Antwort

9

Ich kann die Ergebnisse auf meinem gcc (Ubuntu 5.2.1-22ubuntu2) bestätigen. Was zu passieren scheint ist, dass der 32-Bit nicht optimierte Code 387 FPU mit FMUL Opcode verwendet, während 64-Bit den SSE MULS Opcode verwendet. (Führen Sie einfach gcc -S test.c mit verschiedenen Parametern aus und sehen Sie den Assembler-Ausgang). Und wie bekannt ist, hat die 387 FPU, die die FMUL ausführt, mehr als 64 Bits Präzision (80!), So scheint es, dass es hier anders Runden. Der Grund ist natürlich, dass der genaue Wert von 64-Bit-IEEE Doppel 210.01 ist das nicht, aber

210.009999999999990905052982270717620849609375 

und wenn man mit 1000 multiplizieren, sind Sie eigentlich nicht nur die Dezimalpunkt Verschiebung - schließlich gibt ist kein Dezimalpunkt, sondern Binärpunkt im Fließkommawert; Also muss der Wert gerundet werden. Und bei 64-Bit-Doppel ist es aufgerundet. Bei 80-Bit-387-FPU-Registern ist die Berechnung präziser und es wird abgerundet down.

Nachdem ich etwas mehr gelesen habe, glaube ich, dass das Ergebnis, das von GCC auf 32-Bit-Arch erzeugt wird, nicht standardkonform ist. Wenn Sie also den Standard zu C99 oder C11 zwingen, mit -std=c99, -std=c11, erhalten Sie das richtige Ergebnis

% gcc -m32 -std=c11 test.c; ./a.out 
210010 

Wenn Sie wollen nicht mit Gewalt C99 oder C11-Standard, können Sie auch den -fexcess-precision=standard Schalter verwenden.


Aber Spaß hört hier nicht auf.

% gcc -m32 test.c; ./a.out 
210009 
% gcc -m32 -O3 test.c; ./a.out 
210010 

So erhalten Sie die "richtige" führen, wenn Sie mit -O3 kompilieren; Das liegt natürlich daran, dass der 64-Bit-Compiler die 64-Bit-SSE-Mathematik verwendet, um die Berechnung konstant zu falten.


Um die zusätzliche Genauigkeit wirkt sich dies zu bestätigen, Sie ein long double verwenden:

#include "stdio.h" 
#include <stdint.h> 

int main() 
{ 
    long double d1 = 210.01; // double constant to long double! 
    uint32_t m = 1000; 
    uint32_t v1 = (uint32_t) (d1 * m); 

    printf("%d",v1); 
    return 0; 
} 

Jetzt noch -m64 Runden es 210009.

% gcc -m64 test.c; ./a.out 
210009 
Verwandte Themen