2012-03-26 10 views
3

ich ', diesen einfachen Code versuchen. Es zeigt die ersten 10 ganzen Zahlen, die nicht in float dargestellt werden können:(Float) Casting funktioniert nicht

int main(){ 
    int i, cont=0; 
    float f; 
    double di, df; 
    for(i=10000000, f=i; i<INT_MAX; i++, f=i, df=f, di=((float)i)){ 
    if(i!=f){ 
     printf("i=%d f=%.2f df=%.2lf di=%.2lf\n", i, f, df, di); 
     if(cont++==10) return 0; 
    } 
    } 
    return 1; 
} 

di ist eine doppelte variable, aber ich stelle es auf (float) i, so dass es gleich sein sollte, df, aber es ist nicht.

Zum Beispiel kann die Zahl 16.777.217 als 16777216 durch f dargestellt unddf, aber di noch 16.777.217, ignoriert den (float) Guss.

Wie ist das möglich?

** Ich bin mit diesem: gcc (Ubuntu 4.4.3-4ubuntu5) 4.4.3

+1

Welche Plattform und Compiler-Version? – sarnold

+3

Warum benutzen Sie den Komma-Operator so? Es gibt absolut keinen Bedarf und fügt unnötige Komplexitätsfragen wie diese hinzu. – Joe

+0

Sie müssen nicht '% lf' sagen. Nur '% f' ist in Ordnung, und es bedeutet' double'. –

Antwort

2

zu Ihrer Frage ist 6.3.1.8:2 in the C99 standard:

Die Werte des Schwebens Operanden und die Ergebnisse von schwebenden Ausdrücken können mit größerer Genauigkeit und einem größeren Bereich als dargestellt werden, die für den Typ erforderlich sind; Die Typen werden dadurch nicht verändert.

und insbesondere Fußnote 52:

Die gegossenen und Zuweisungsoperatoren sind weiterhin in 6.3.1.4 und 6.3.1.5 beschrieben, ihre angegebenen Umwandlungen auszuführen erforderlich.

Lesen der Fußnote, ich würde sagen, dass Sie einen Fehler in Ihrem Compiler identifiziert haben.

Möglicherweise haben Sie in Ihrem Compiler zwei Bugs erkannt: Der i!=f Vergleich wird zwischen Floats durchgeführt (siehe Promotionsregeln auf derselben Seite des Standards), daher sollte er immer falsch sein. Obwohl ich in diesem letzteren Fall denke, dass der Compiler möglicherweise einen größeren Typ für den Vergleich von 6.3.1.8: 2 verwenden darf, vielleicht den Vergleich gleich (double)i!=(double)f und damit manchmal wahr. Absatz 6.3.1.8:2 ist der Absatz in dem Standard, den ich am meisten hasse, und ich versuche immer noch, strenges Aliasing zu verstehen.

+0

Vielen Dank.Ich denke, Sie haben Recht und es gibt einen Fehler (oder Funktion?) Im Compiler, wie ich! = F ist zwischen Doubles statt Floats verglichen. – salteador

+0

Dies ist ein extrem langjähriger bekannter Fehler in gcc, den die Entwickler nur schwer beheben können, da er die Leistung für Leute, denen die Korrektheit egal ist, mäßig beeinträchtigt.Wenn Sie Ihr Programm nicht mit alten CPUs inkompatibel machen wollen, können Sie das Problem mit '-msse' beheben (verwenden Sie SSE anstelle von FPU für Fließkomma). Beachten Sie, dass das Problem auf anderen Plattformen als x86 (und vielleicht m68k?) Nicht existiert. –

+0

@R ..: Wenn sie die Leistung ohne Rücksicht auf die Korrektheit maximieren wollen, warum nicht einfach alle Fließkommaberechnungen 42.0 zurückgeben? Wäre das nicht schneller als vorzutäuschen, Mathe zu machen? Es ist gut, IMHO, dass ein Compiler Dinge wie 'f4 = f1 + f2 + f3;' ausführt, indem er für die Zwischenwerte ein 80-Bit-Extended-Format anstelle des 32-Bit-Formats verwendet *, vorausgesetzt, es ist konsistent in der Handhabung solcher Dinge *, aber wenn Vergleiche und Umwandlungen nicht auf den richtigen Typen gemacht werden, sind sie bedeutungslos und "Geschwindigkeit" ist irrelevant. – supercat

2

Dieser Beitrag erklärt, was los ist:

http://www.exploringbinary.com/when-floats-dont-behave-like-floats/

Grundsätzlich zusätzliche Präzision könnte für verschiedene Ausdruckauswertungen auf der Maschine gespeichert werden, so dass, was gleich schwebt nicht gleich sein würde.

+0

Vielen Dank. Ich wusste das nicht;) – salteador

+0

Der Standard erfordert Abgüsse, um jede Extrapräzision wegzuwerfen. GCC ist in dieser Hinsicht einfach gebrochen, und es ist ein bekanntes Problem. Das Problem kann teilweise mit der Option "-ffloat-store" behoben werden, aber das behebt nur Zuweisungen, keine Umwandlungen. –