2014-02-23 2 views
11

Das Ergebnis des folgenden Programms ist mir auf meinem Rechner etwas seltsam.Warum scheint der Wert von double nach der Zuweisung geändert zu werden?

#include <iostream> 

using namespace std; 

int main(){ 
    double a = 20; 
    double b = 0.020; 
    double c = 1000.0; 

    double d = b * c; 

    if(a < b * c) 
     cout << "a < b * c" << endl; 

    if(a < d) 
     cout << "a < d" << endl; 

    return 0; 
} 

Ausgang:

$ ./test 
a < b * c 

ich doppelt weiß, ist nicht, dass genau wegen der Präzision. Aber ich erwarte nicht, dass sich der Wert ändert und ein inkonsistentes Vergleichsergebnis ergibt. Wenn die a < b * c ausgedruckt wird, erwarte ich, dass a < d auch gedruckt werden sollte. Aber wenn ich diesen Code auf meinem i686-Server und sogar auf meinem Cygwin laufen lasse. Ich kann a < b * c sehen, kann aber a < d nicht sehen.

Es wurde bestätigt, dass dieses Problem plattformabhängig ist. Wird dies durch die unterschiedliche Instruktion und Durchführung der Doppelbelegung verursacht?

UPDATE

Die generierte Assembly:

main: 
.LFB1482: 
    pushl %ebp 
.LCFI0: 
    movl %esp, %ebp 
.LCFI1: 
    subl $56, %esp 
.LCFI2: 
    andl $-16, %esp 
    movl $0, %eax 
    subl %eax, %esp 
    movl $0, -8(%ebp) 
    movl $1077149696, -4(%ebp) 
    movl $1202590843, -16(%ebp) 
    movl $1066695393, -12(%ebp) 
    movl $0, -24(%ebp) 
    movl $1083129856, -20(%ebp) 
    fldl -16(%ebp) 
    fmull -24(%ebp) 
    fstpl -32(%ebp) 
    fldl -16(%ebp) 
    fmull -24(%ebp) 
    fldl -8(%ebp) 
    fxch %st(1) 
    fucompp 
    fnstsw %ax 
    sahf 
    ja .L3 
    jmp .L2 

    //.L3 will call stdout 
+1

Vielleicht hat es etwas mit ständiger Optimierung zu tun. Kannst du die Assembly zeigen oder reproduzieren, wenn du 'a',' b' und 'c' von' std :: cin' nimmst? –

+0

Keine Fehler/Warnungen vom Compiler. – StarPinkER

+0

Ich kann es reproduzieren Wenn ich es von Std :: Cin bekomme, werde ich die Montage später veröffentlichen. @NateKohl – StarPinkER

Antwort

5

Hypothese: Sie die Auswirkungen werden die 80-Bit-Intel-FPU sehen.

Mit der Definition double d = b * c wird die Menge b * c mit 80-Bit-Genauigkeit berechnet und auf 64-Bit gerundet, wenn sie in d gespeichert wird. (a < d) würde die 64-Bit a mit 64-Bit d vergleichen.

OTOH, mit dem Ausdruck (a < b * c), Sie haben ein 80-Bit-arithmetisches Ergebnis b * c direkt gegenüber a vor dem Verlassen der FPU verglichen. Daher wird die Genauigkeit des b*c -Ergebnisses nie durch Speichern in einer 64-Bit-Variablen abgeschnitten.

Sie müssen sich die generierten Anweisungen ansehen, um sicher zu sein, und ich erwarte, dass dies mit den Compiler-Versionen und Optimizer-Flags variieren wird.

+0

Siehe http://Stackoverflow.com/q/20869904/420683 – dyp

+0

Ich versuchte, hier x87 einzustellen http://coliru.stacked-crooked.com/a/3022f77c07303e32, aber es machte keinen Unterschied. Ich bekomme keine Ausgabe mit x87 oder SSE. Das negiert nichts, was Sie gesagt haben, aber ich bin nur neugierig, welche Compiler-Flags ich einstellen muss, um den Effekt zu sehen. –

3

Ich bin nicht sicher, welche Art von Hardware eine AS3-Maschine ist, aber Sie können dieses Verhalten zum Beispiel in Maschinen sehen, wo die interne Gleitkommaeinheit größere als 64-Bit-Floats verwendet, um Zwischenergebnisse zu speichern. Dies ist der Fall im x86-Arch mit den Gleitkommaeinheiten x87 (aber nicht mit SSE).

Das Problem ist, dass der Prozessor in b und c zu Gleitkommaregister geladen wird, dann die Multiplikation tun und das Zwischenergebnis in einem Register speichern. Wenn dieses Register größer als 64 Bits ist, wird das Ergebnis anders sein als d (oder a), die berechnet und in den Speicher zurückgespeichert wurden, was sie zu 64 Bits zwang.

Dies ist ein Szenario von vielen, müssten Sie Ihren Assembly-Code betrachten, um genau zu bestimmen, was vor sich geht. Sie müssen auch verstehen, wie Ihre Hardware intern mit Gleitkommaberechnungen umgeht.

+0

Ich denke, Sie haben Recht. Aber heißt das, wenn man a und das Zwischenergebnis vergleicht, wird der Zwischenwert nicht in den Speicher geladen, sondern in den fpu geladen und dort verglichen. – StarPinkER

1

Ein schneller Test des Codes mit MinGW auf meiner Windows-Maschine produziert genau dieselben Ergebnisse. Was wirklich merkwürdig ist, ist, dass, wenn ich die Doppelgänger in Floats umwandle, alles perfekt läuft, so wie es sollte (überhaupt keine Ausgabe). Wenn ich sie jedoch zu langen Doppeln ändere, erscheinen sowohl "a < b * c" als auch "a < d".

Meine Vermutung ist vielleicht, da Doppelgänger mehr Präzision ermöglichen sollen, etwas Seltsames passiert, wenn man die beiden unmittelbaren Werte multipliziert und einen Vergleich durchführt, statt das Ergebnis für später zu speichern? Das würde auch erklären, warum das Problem schließlich auch mit langen Doppeln auftritt, da sie noch mehr Speicherplatz benötigen würden.

Verwandte Themen