9

So habe ich eine Funktion, die etwa wie folgt aussieht:Kann man Variablen irgendwie verändern?

float function(){ 
    float x = SomeValue; 
    return x/SomeOtherValue; 
} 

An einem gewissen Punkt, diese Funktion überläuft und gibt einen wirklich großen negativen Wert. Um zu versuchen, die Spur zu kommen, wo genau dies geschah, habe ich eine cout-Anweisung so, dass die Funktion sah wie folgt aus:

float function(){ 
    float x = SomeValue; 
    cout << x; 
    return x/SomeOtherValue; 
} 

und es funktionierte! Natürlich habe ich das Problem mit einem Doppel gelöst. Aber ich bin neugierig, warum die Funktion richtig funktionierte, als ich es ansprach. Ist das typisch, oder könnte es irgendwo anders einen Fehler geben, den ich vermisse?

(Wenn es keine Hilfe ist, wird der Wert in dem Schwimmer gespeichert ist nur ein Integer-Wert, und keine besonders großen. Ich kann es nur in einem Schwimmer setzen Gießen zu vermeiden.)

Antwort

18

Willkommen in der wunderbaren Welt der Fließkommazahl. Die Antwort, die Sie erhalten, hängt wahrscheinlich vom Fließkommamodell ab, mit dem Sie den Code kompiliert haben.

Dies geschieht aufgrund des Unterschieds zwischen der IEEE-Spezifikation und der Hardware, auf der der Code ausgeführt wird. Ihre CPU hat wahrscheinlich 80-Bit-Gleitkommaregister, die verwendet werden, um den 32-Bit-Gleitkommawert zu halten. Dies bedeutet, dass es viel mehr Genauigkeit gibt, während der Wert in einem Register bleibt, als wenn er zu einer Speicheradresse gezwungen wird (auch bekannt als "Referenzieren" des Registers).

Wenn Sie den Wert an cout übergeben, musste der Compiler den Fließkommawert in den Speicher schreiben, und dies führt zu einem Verlust von WRT-Überlauffällen mit Präzision und interessantem Verhalten.

Siehe die MSDN-Dokumentation zu VC++ floating point switches. Sie könnten versuchen, mit/fp zu kompilieren: strikt und sehen, was passiert.

+0

Es gibt auch einen GCC-Hinweis dafür unter http://gcc.gnu.org/wiki/x87note Aufgrund dieses wunderbaren Verhaltens ist der Vergleich von Gleitkommaberechnungen ebenfalls inhärent fehlerhaft, sofern nicht vorberechnete Werte verwendet werden. – hazzen

3

Wert Drucke auf cout sollte den Wert des Parameters in keiner Weise ändern.

Allerdings habe ich ähnliches Verhalten gesehen, Hinzufügen von Debugging-Anweisungen verursacht eine Änderung des Werts. In diesen Fällen, und wahrscheinlich auch in diesem Fall, vermutete ich, dass die zusätzlichen Anweisungen dazu führten, dass sich der Optimierer des Compilers anders benahm, also generiere anderen Code für deine Funktion.

Das Hinzufügen der Anweisung cout bedeutet, dass der Wert von x direkt verwendet wird. Ohne sie könnte der Optimierer die Variable entfernen, wodurch die Reihenfolge der Berechnung geändert und somit die Antwort geändert wird.

0

Ich glaube nicht, dass die Cout hat einen Einfluss auf die Variable, das Problem müsste woanders sein.

2

Als beiseite, es ist immer eine gute Idee, unveränderliche Variablen deklarieren const mit:

float function(){ 
    const float x = SomeValue; 
    cout << x; 
    return x/SomeOtherValue; 
} 

unter anderem das Sie versehentlich verhindern Ihre Variablen Funktionen auf, sie über nicht const Referenzen ändern kann .

1

cout verursacht einen Verweis auf die Variable, was dazu führt, dass der Compiler die Übertragung auf den Stack erzwingt.

Da es sich um ein Float handelt, führt dies wahrscheinlich dazu, dass der Wert von der doppelten oder langen doppelten Darstellung abgeschnitten wird, die es normalerweise hätte.

Aufruf einer Funktion (nicht-inlined), die einen Zeiger oder Verweis auf x nimmt, sollte am Ende das gleiche Verhalten verursachen, aber wenn der Compiler später intelligenter wird und lernt, es zu inline, werden Sie gleichermaßen geschraubt sein :)