Frage

Ich hatte einen kleinen WTF Moment an diesem Morgen. Ths WTF kann mit dieser zusammenfassen:

float x = 0.2f;
float y = 0.1f;
float z = x + y;
assert(z == x + y); //This assert is triggered! (Atleast with visual studio 2008)

Der Grund scheint zu sein, dass der Ausdruck x + y und mit der verkürzten Version in z im Vergleich zu verdoppeln gefördert wird. (Wenn ich z ändern, um die assert double nicht ausgelöst wird).

Ich kann für Präzisions Gründe sieht, dass es sinnvoll wäre, all Floating-Point-Arithmetik in doppelter Genauigkeit durchzuführen, bevor das Ergebnis in einfacher Genauigkeit konvertieren. Ich fand den folgenden Absatz in der Norm (was ich denke, ich irgendwie schon wusste, aber nicht in diesem Zusammenhang):

4.6.1. „Ein R-Wert vom Typ float kann zu einem R-Wert vom Typ double umgewandelt werden. Der Wert ist unverändert“

Meine Frage ist, x + y garantiert gefördert wird am Compiler Ermessen zu verdoppeln oder ist?

UPDATE: Da viele Menschen haben behauptet, dass man nicht == für Floating-Point verwenden soll, wollte ich nur, dass ich mit gerade arbeitete im konkreten Fall erklären, ein genauer Vergleich ist gerechtfertigt.

Gleitkomma-Vergleich ist heikel, hier ist eine interessante Link rel="nofollow über das Thema, das ist denke ich nicht erwähnt.

War es hilfreich?

Lösung

Sie können nicht generell davon ausgehen, dass == für Gleitkomma-Typen wie erwartet. Vergleichen gerundete Werte oder verwenden Konstrukte wie abs(a-b) < tolerance statt.

Promotion ist ganz im Ermessen des Compilers (und wird auf Zielhardware, Optimierungsstufe abhängen, etc).

Was geht in diesem speziellen Fall auf ist fast sicher, dass die Werte in der FPU-Register mit einer höheren Genauigkeit gespeichert werden als in Speicher - in der Regel Hardware moderne FPU arbeitet mit doppelter oder höherer Präzision intern, was Präzision der Programmierer gefragt, mit der Compiler Code generiert, um die entsprechenden Umwandlungen, wenn die Werte in dem Speicher gespeichert werden; in einem nicht optimiert bauen, ist das Ergebnis der x+y noch in einem Register an der Stelle des Vergleich durchgeführt wird, aber z wird in dem Speicher gespeichert worden ist und zurückgeholt, und somit abgeschnitten Präzision zu schweben.

Andere Tipps

Der Arbeitsentwurf für das nächste Standard-C ++ 0x Abschnitt 5 Nummer 11 sagt

  

Die Werte der schwimmenden Operanden und die Ergebnisse der schwimmenden Ausdrücke können als die von der Art erforderlich, in einem höheren Präzision und Reichweite dargestellt werden; die Typen werden dadurch nicht geändert

So im Ermessen des Compilers.

Mit gcc 4.3.2, ist die Behauptung nicht ausgelöst, und in der Tat, der R-Wert zurück von x + y ist ein float, sondern als ein double.

So ist es bis zu den Compiler ist. Deshalb ist es nicht klug, auf exakte Gleichheit zwischen zwei Gleitkommawerte verlassen.

Die C ++ FAQ Lite hat einige weitere Diskussion zum Thema:

Es ist das Problem, da float Nummer Binärkonversion nicht genau Genauigkeit nicht geben.

Und innerhalb sizeof(float) Bytes kann es nicht genauen Wert der Fließkommazahl und arithmetischer Operation unterbringen kann zu einer Annäherung führen und damit die Gleichheit nicht.

Im folgenden sehen Sie z.

float x = 0.25f; //both fits within 4 bytes with precision
float y = 0.50f;
float z = x + y;
assert(z == x + y); // it would work fine and no assert

Ich würde denken, dass es an den Compiler Ermessen wäre, aber man konnte es immer mit einer Besetzung zwingen, wenn das Ihr Denken ist?

Ein weiterer Grund, nie direkt Schwimmern vergleichen.

if (fabs(result - expectedResult) < 0.00001)
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top