float - Operation liefert merkwürdige Ergebnisse
-
Hallo zusammen,
leider kann ich mir nicht erklaeren, warum die ersten beiden printf-Anweisung einen anderen Wert anzeigen als die 3.printf-Anweisung.
Weiß jemand Rat?#include <stdio.h> int main(void) { float x=1.0f, y=1e-9f; printf ("x+y : %30.15f\n", x+y); printf ("(float)(x+y): %30.15f\n", (float)(x+y) ); printf ("1.0f + 1E-9f: %30.15f\n", 1.0f + 1e-9f); return 0; }
Ergebnisse:
x+y : 1.000000001000000
(float)(x+y): 1.000000001000000
1.0f + 1E-9f: 1.000000000000000Gleiche Ergebnisse sowohl mit gcc unter Linux als auch mit gcc(Cygwin) unter Windows Xp, 32bit.
Ich hätte auch bei den ersten beiden das letze Ergebnis erwartet wegen des Datentyps float. Werden da implizite Typumwandlungen nach double vorgenommen? Wenn ja, warum?
Viele Grüße
peule
-
Ich würde behaupten es liegt daran, dass in den ersten beiden printfs das Ergebnis der Addition in einem Register* liegt, beim letzten printf allerdings eine (zur Compilezeit berechnete) Größe liegt.
*das auf deinem System eine höhere Genauigkeit als ein float hat
Edit: PS: beim gcc könnte dich der Schalter -ffloat-store interessieren
PPS: Mit hoher Optimierung eingeschaltet dürfte auch bei allen das gleiche rauskommen, da dort sämtliche Rechnungen gleich rausoptimiert werden sollten.
-
habs compiliert mit -O -O2 O3 und -ffloat-store.
Leider auch keine Änderung.Besonders wundert es mich, dass auch der Cast keinerlei wirkung zeigt.
Viele Grüße
Sönke
-
Zeig mal bitte den Assembler-Output (Switch -S) bei voller Optimierung (-O3).
-
im 3.fall handelt es sich offensichtlich um einen double datentyp, dies erklärt alles. wie man da irgendwas von registern faseln kann ist mir völlig schleierhaft...
-
.file "test.c" .def ___main; .scl 2; .type 32; .endef .section .rdata,"dr" LC2: .ascii "x+y : %30.15f\12\0" LC3: .ascii "(float)(x+y): %30.15f\12\0" LC4: .ascii "1.0f + 1E-9f: %30.15f\12\0" .text .p2align 4,,15 .globl _main .def _main; .scl 2; .type 32; .endef _main: pushl %ebp movl $16, %eax movl %esp, %ebp subl $24, %esp andl $-16, %esp call __alloca call ___main movl $LC2, (%esp) movl $0x3089705f, %eax movl $0x3f800000, %edx movl %eax, -8(%ebp) movl %edx, -4(%ebp) flds -8(%ebp) fadds -4(%ebp) fstpl 4(%esp) call _printf flds -8(%ebp) fadds -4(%ebp) movl $LC3, (%esp) fstpl 4(%esp) call _printf movl $LC4, (%esp) fld1 fstpl 4(%esp) call _printf leave xorl %eax, %eax ret .def _printf; .scl 3; .type 32; .endef
kompiliert: gcc test.c -O3 -ffloat-store -S
-
Bajou schrieb:
im 3.fall handelt es sich offensichtlich um einen double datentyp, dies erklärt alles. wie man da irgendwas von registern faseln kann ist mir völlig schleierhaft...
Das einzige was hier offensichtlich ist: Du hast keine Ahnung bzw. du bist ein Troll. Aber du kannst mir mal 2 Dinge erklären:
- Warum hat der OP im 3. Fall trotz des deiner Meinung nach genaueren doule-Typs eine ungenaueres Ergebnis?
- Warum sollte der Ausdruck im 3. Fall den Typ double haben?@ feraL: Nett gemeint, aber eigentlich wollte ich das vom OP. Und dann am besten einmal ohne Optimierung, einmal mit O3 und einmal mit -ffloat-store und O3. Natürlich auch den Textoutput mit angeben.
-
Also in allen 3 Fällen werden 2 Float-Größen addiert.
Im ersten und 2. Fall ist das Ergebnis dieser Addition wohl vom Typ double,
erkennbar für mich am genaueren Ergebnis.
Die Addition der beiden Float-Konstanten im 3. Fall liefert ein Ergebnis vom Typ float wegen des ungenaueren Ergebnisses.Gibts da keine allgemeine Regel, wie mit 2 float-Operanden bei einer arithmetischen Operation verfahren wird? Hab was gelesen, dass in ANSI-C arithmetische Operationen in double vorgenommen werden, dann passt aber das 3. Ergebnis nicht.
Rätselhaft ....
-
Tim, Du hast recht!
Beim Übersetzen mit
gcc -O3 oder gcc -ffloat-store sind alle 3 Ergebnisse identisch:x+y : 1.000000000000000
(float)(x+y): 1.000000000000000
1.0f + 1E-9f: 1.000000000000000Hab wohl vorher irgendwas falsch gemacht, bitte um Entschuldigung.
Fazit: die Regel heisst also:
Bei Operanden vom Typ float ist das Ergebniss auch vom Typ float.Ggf. kann durch Registeroperationen eine höhere Genauigkeit erreicht werden, wenn das System das hergibt, wie Du in Deiner ersten Antwort geschrieben hast.
Vielen Dank für Deine Hilfe!
Gruß
peule
-
peule schrieb:
Fazit: die Regel heisst also:
Bei Operanden vom Typ float ist das Ergebniss auch vom Typ float.naja, compiler machen manchmal 'promotions' und 'arthmetic conversions' um die genauigkeit von berechnungen zu verbessern. wann sowas zuschlägt steht irgendwo im c-standard. jedenfalls würde ich nicht davon ausgehen, dass (float)a+float(b) das selbe ist wie (float)(a+b).
-
Sind a und b bei dir denn auch floats? Such mir bitte mal die Stelle im Standard raus. Ich finde sie nicht.
-
Tim schrieb:
Such mir bitte mal die Stelle im Standard raus. Ich finde sie nicht.
wenn du's nicht findest, dann hab' ich mich wohl getäuscht. floats und doubles traue ich trotzdem nicht über'n weg.
-
~fricky schrieb:
Tim schrieb:
Such mir bitte mal die Stelle im Standard raus. Ich finde sie nicht.
wenn du's nicht findest, dann hab' ich mich wohl getäuscht.
Es gibt die "usual arithmetic conversions". Die sagen z.B. dass bei
1.0 + 1.0f
der float-Operand nach double gewandelt wird, aber bei1.0f + 1.0f
findet nichts mehr statt.~fricky schrieb:
floats und doubles traue ich trotzdem nicht über'n weg.
Ja, aber sie machen das Leben manchmal deutlich einfacher.
-
Tim schrieb:
~fricky schrieb:
floats und doubles traue ich trotzdem nicht über'n weg.
Ja, aber sie machen das Leben manchmal deutlich einfacher.
nunja, für numerisch/mathematische anwendungen, wobei man die feine auflösung braucht, sind sie gut. trotzdem muss man sich mit rundungsfehlern rumplagen. und wenn floats emuliert werden, ist's zudem noch langsam. für die meisten fälle ist doch die mod 2^registerbreiten-arithmetik (integer) einfach besser.