Gleitkommaanomalie, wenn eine nicht verwendete Anweisung nicht auskommentiert ist?

8

Wenn das Programm wie unten gezeigt ausgeführt wird, erzeugt es eine ok-Ausgabe:

%Vor%

Aber wenn die auskommentierte Zeile ( d. h. //if (argc>1) r = atol(argv[1]); ) unkommentiert ist, wird Folgendes erzeugt:

%Vor%

, obwohl diese Zeile keine Auswirkung haben sollte, da argc>1 falsch ist. Hat jemand eine plausible Erklärung für dieses Problem? Ist es auf anderen Systemen reproduzierbar?

%Vor%

Hinweis, Testsystem = AMD Athlon 64 5200+ System mit Linux 2.6.35.14-96.fc14.i686 ( ie , gestartet zum Betrieb eines 32-Bit-Betriebssystems auf 64-Bit-HW) mit gcc (GCC) 4.5.1 20100924 (Red Hat 4.5.1-4)

Update - Vor ein paar Stunden habe ich einen Kommentar gepostet, dass Code, der mit und ohne die if -Anweisung generiert wurde, sich nur in Stack-Offsets und etwas übersprungenem Code unterscheidet. Ich finde jetzt, dass der Kommentar nicht ganz korrekt war; d. h. gilt für nicht optimierten Code, nicht jedoch für den von mir ausgeführten -O3-Code.

Effekt der Optimierung schaltet das Problem ein:

  • -O0: Beide Programmversionen laufen ok
  • -O2 oder -O3: Version mit Kommentar hat den obigen Fehler, wobei j=20000 und k=17285
  • -O1: Version mit Kommentar hat j=20000 (ein Fehler) und k=0 (OK)

Jedenfalls unterscheiden sich die beiden Fälle bei Code-Listings-O3 -S hauptsächlich in übersprungenen if -Code- und Stack-Offsets bis zur Zeile vor call floor , wobei der with-if-Code dann noch einen% co_de hat % als der Ohne-If-Code:

%Vor%

Ich habe den Grund für den Unterschied nicht herausgefunden.

    
James Waldby - jwpat7 24.11.2011, 02:27
quelle

4 Antworten

3

Nicht auf meinem System dupliziert, Win7 läuft CygWin mit gcc 4.3.4. Sowohl mit als auch ohne die if -Anweisung wird der Wert von j auf Null gesetzt, nicht auf 20K.

Mein einziger Vorschlag wäre, gcc -S zu verwenden, um sich die Assembler-Ausgabe anzusehen. Das sollte dir hoffentlich sagen, was schief läuft.

Insbesondere generieren Sie die Assembler-Ausgabe in zwei separate Dateien, jeweils eine für die funktionierende und nicht arbeitende Variante, dann vgrep sie (Augapfel sie Seite an Seite), um zu versuchen, den Unterschied zu ermitteln.

Dies ist übrigens ein schwerwiegender Fehler in Ihrer Umgebung. Wenn m 10000 ist, bedeutet das, dass x - floor(x) gleich 2 sein muss. Ich kann nicht für das Leben von mir an eine reelle Zahl denken, wo das der Fall wäre: -)

    
paxdiablo 24.11.2011 02:35
quelle
2

Ich denke, es gibt zwei Gründe, warum diese Zeile einen Effekt haben könnte:

  • Ohne diese Zeile können die Werte all dieser Variablen zur Kompilierungszeit bestimmt werden (und, IMHO, höchstwahrscheinlich sind ); Mit dieser Zeile müssen die Berechnungen zur Laufzeit ausgeführt werden. Aber offensichtlich sind die vorberechneten Werte des Compilers die gleichen wie die Werte, die zur Laufzeit berechnet werden, und ich neige dazu, dies als den tatsächlichen Grund für das unterschiedliche beobachtete Verhalten zu betrachten. (Es würde sich aber sicherlich als großer Unterschied in der Assembler-Ausgabe zeigen!)
  • Auf vielen Maschinen wird Fließkomma-Arithmetik durchgeführt, wobei mehr Bits in Zwischenwerten verwendet werden, als tatsächlich in einer Gleitkommazahl mit doppelter Genauigkeit gespeichert werden können. Ihre zweite Version, indem Sie zwei verschiedene Code-Pfade erstellen, um x zu setzen, schränkt im Prinzip x auf das ein, was in einer Gleitkommazahl mit doppelter Genauigkeit gespeichert werden kann, während Ihre erste Version dies zulässt der anfänglich berechnete Wert für x steht immer noch als ein Zwischenwert mit zusätzlichen Bits zur Verfügung, wenn nachfolgende Werte berechnet werden. (Dies kann der Fall sein, wenn alle diese Werte zur Kompilierungszeit oder zur Laufzeit berechnet werden.)
ruakh 24.11.2011 02:58
quelle
2

Der Grund für das Auskommentieren dieser Zeile könnte sich auf das Ergebnis auswirken: Ohne diese Zeile kann der Compiler sehen, dass r und jroot sich nach der Initialisierung nicht ändern können, sodass x berechnet werden kann. zur Kompilierungszeit anstatt zur Laufzeit. Wenn die Zeile unkommentiert ist, kann r sich ändern, daher muss die Berechnung von x auf die Laufzeit verschoben werden, was dazu führen kann, dass sie mit einer anderen Genauigkeit ausgeführt wird (insbesondere wenn 387 Fließkomma-Berechnungen verwendet werden).

Sie können versuchen, -mfpmath=sse -march=native zu verwenden, um die SSE-Einheit für Fließkommaberechnungen zu verwenden, die keine übermäßige Genauigkeit aufweist. oder Sie können versuchen, den Schalter -ffloat-store zu verwenden.

Ihre Subtraktion x - floor(x) weist eine katastrophale Aufhebung auf - das ist die Grundursache des Problems etwas zu vermeiden;).

    
caf 24.11.2011 02:53
quelle
0

BEARBEITET:

Ich sehe auch keinen Unterschied, wenn ich meinen Code auf meinem Computer unter Verwendung der -OO, -O1, -O2 und -O3 kompiliere.

AMD Phenom Quad 64 Bit. gcc (Ubuntu 4.4.3-4ubuntu5) 4.4.3

Ich habe auch clang (llvm) ab Release 3.0 mit und ohne die gleichen Ergebnisse versucht.

Ich stimme zu, dass der Compiler alles ohne die if-Zeile vorberechnen kann, aber Sie würden das definitiv in der Assembly-Ausgabe sehen.

Fließkomma und C können sehr unangenehm sein, viele Dinge, die man wissen muss, damit es wirklich funktioniert. Es ist gut für die Genauigkeit, die int zu doppelten Konvertierungen zu zwingen (c-Bibliotheken im Compiler, selbst wenn die fpu gut ist, haben Probleme und die Compiler-C-Bibliothek, die sie benutzt und die C-Bibliothek, kompiliert oder benutzt von Ihrem Programm kann / will different), aber int to / from float ist, wo FPUs ihre Fehler haben (ich glaube, ich habe das bei TestFloat oder irgendwo so gesehen). Könnte versuchen, TestFloat auf Ihrem System laufen zu lassen, um zu sehen, ob Ihre FPU gut ist. Zwischen dem berühmten Pentium-Gleitkomma-Bug und dem Pentium IV und darüber hinaus hatten die meisten Prozessoren Gleitkomma-Bugs, das Pentium III war solide, aber der Pentium IV, den ich hatte, würde scheitern. Ich verwende selten Fließkommazahl mehr, also nicht die Mühe, meine Systeme zu testen.

Beim Spielen mit der Optimierung wurden die Ergebnisse entsprechend Ihrer Bearbeitung geändert, daher handelt es sich wahrscheinlich um ein gcc-Problem oder eine Kombination aus Code und gcc (und nicht ein Hardware-FPU-Problem). Versuchen Sie dann eine andere Version von gcc auf demselben Computer. B. 4.4.x statt 4.5.x.

    
old_timer 24.11.2011 07:03
quelle