Rechenprogramm liefert "machmal" falsche Werte
-
@hustbaer sagte in Rechenprogramm liefert "machmal" falsche Werte:
Es könnte sein dass bestimmte Compiler mit bestimmten Einstellungen (fast math und so) darauf "verzichten" die Werte auf das was
float
halten kann zusammenzustauchen. Könnte evtl. auch den Effekt erklären dass mitscanf
was anderes rauskommt als mit Literalen.Danke, genau das wollte ich klarmachen, deswegen habe ich die zwei kleinen conditionals reingemogelt. Einfach mal GETVALUES auskommentieren und float hat Null Bedeutung.
-
@Sarkast sagte in Rechenprogramm liefert "machmal" falsche Werte:
Einfach mal GETVALUES auskommentieren und float hat Null Bedeutung.
Wir sind hier bei Standart-C++. Und da stimmt eben Deine Behauptung eben einfach nicht.
-
@Swordfish sagte in Rechenprogramm liefert "machmal" falsche Werte:
@Sarkast sagte in Rechenprogramm liefert "machmal" falsche Werte:
Einfach mal GETVALUES auskommentieren und float hat Null Bedeutung.
Wir sind hier bei Standart-C++. Und da stimmt eben Deine Behauptung eben einfach nicht.
Das beobachtete Verhalten ist perfekt standardkonform.
-
@SeppJ sagte in Rechenprogramm liefert "machmal" falsche Werte:
Das beobachtete Verhalten ist perfekt standardkonform.
Auf welchen Post beziehst Du Dich?
Ich habe von der Behauptung gesprochen daß es einen Unterschied machen würde ob irgendwas zur Compiletime oder zur Runtime berechnet wird oder woher die Werte dazu kommen (user input vs. initializer).
-
@Swordfish sagte in Rechenprogramm liefert "machmal" falsche Werte:
Ich habe von der Behauptung gesprochen daß es einen Unterschied machen würde ob irgendwas zur Compiletime oder zur Runtime berechnet wird oder woher die Werte dazu kommen (user input vs. initializer).
Auf das hier. Aber ich muss mich selber korrigieren. In C++11 wurde es geändert, so dass laut Standard nun Casts und Zuweisungen zu einer Präzisionsänderung führen müssen. Diese Änderung war zu obskur, als dass ich sie seit 2011 jemals mitbekommen habe. Praktisch hält sich ja auch gar kein Compiler an diese Vorgabe, außer man zwingt ihn mittels Compilerschaltern dazu.
Siehe
https://en.cppreference.com/w/cpp/types/climits/FLT_EVAL_METHOD
und
https://en.cppreference.com/w/cpp/preprocessor/impl
-
@SeppJ sagte in Rechenprogramm liefert "machmal" falsche Werte:
Praktisch hält sich ja auch gar kein Compiler an diese Vorgabe
Du möchtest mir sagen, daß es Compiler gibt (nein, nicht mit irgendwelchen Switches) bei denen in
float foo = 0.42 * 21;
dasfoo
plötzlich double precision hat!?
-
@Swordfish sagte in Rechenprogramm liefert "machmal" falsche Werte:
Du möchtest mir sagen, daß es Compiler gibt (nein, nicht mit irgendwelchen Switches) bei denen in
float foo = 0.42 * 21;
dasfoo
plötzlich double precision hat!?Jain. Ich möchte dir das sagen, was dir auch Sarkast und hustbaer sagen möchten, aber da du denen nicht zuhörst, versuche ich es auch nicht weiter.
-
@SeppJ Ich geh' nochmal lesen.
@SeppJ sagte in Rechenprogramm liefert "machmal" falsche Werte:
aber da du denen nicht zuhörst
Wie kommst Du darauf? Vielleicht die Möglichkeit in Betracht gezogen, daß ich da wirklich etwas nicht verstehe?
-
Ich bin mir nicht sicher, ob Sarkast das richtige meint, je öfter ich es lese, desto eher denke ich, das nicht. Aber das was hustbaer sagte gilt: Wenn Compiler Ausdrücke zur Compilerzeit auswerten, werden sie dabei oft unendliche Präzision benutzen. Auch wenn das nicht strikt standardkonform ist. Dadurch kann was anderes rauskommen, je nachdem, ob man einen Ausdruck mit Compilezeitkonstanten hat, oder ob man die Werte zur Laufzeit einliest. Dummerweise gelingt es mir gerade nicht, ein gutes Beispiel zu produzieren, weil ich nicht unendlich Zeit habe
-
@SeppJ Darf der Compiler
int main(int argc, char *argv[]) { int a = 26, b =27; float hs; hs = a * b * 365.25 * 24 * 60; printf("Ergebnis HS: %f\n", hs); // printf("Ergebnis aus Printf: %f\n", a * b * 365.25 * 24 * 60); return 0; }
durch
int main(int argc, char *argv[]) { puts("369223936.000000"); }
ersetzen ?
oder schreibt er369223920.000000
(den double Wert)?
-
edit: dieses Beispiel ist falsch.
Vielleicht nicht das beste Beispiel, aber mal fix von Stackoverflow inspiriert:#include <stdio.h> int main() { double d = 1234567890*9876543210*123098456876; printf("%f\n", d); int a,b,c; scanf("%d %d %d", &a, &b, &c); double d2 = a*b*c; printf("%f\n", d2); }
Gibt bei mir unterschiedliche Ergebnisse.
-
@SeppJ sagte in Rechenprogramm liefert "machmal" falsche Werte:
9876543210
passt auch nicht mehr in 32-Bit.
-
@DirkB sagte in Rechenprogramm liefert "machmal" falsche Werte:
@SeppJ Darf der Compiler
int main(int argc, char *argv[]) { int a = 26, b =27; float hs; hs = a * b * 365.25 * 24 * 60; printf("Ergebnis HS: %f\n", hs); // printf("Ergebnis aus Printf: %f\n", a * b * 365.25 * 24 * 60); return 0; }
durch
int main(int argc, char *argv[]) { puts("369223936.000000"); }
ersetzen ?
oder schreibt er369223920.000000
(den double Wert)?Ehrlich gesagt, weiß ich nach der ganzen Standardleserei gerade nicht mehr so recht. Ich würde sagen: Vor C++11, ja, nach C++11, nein (oder genauer: C99 war's, auf das sich C++11 bezieht).
@DirkB sagte in Rechenprogramm liefert "machmal" falsche Werte:
@SeppJ sagte in Rechenprogramm liefert "machmal" falsche Werte:
9876543210
passt auch nicht mehr in 32-Bit.
Mist. Ich ziehe dieses spezielle Beispiel zurück. Es gibt aber irgendwelche Kombinationen, wo da ein Unterschied heraus kommt. Das ist der Grund, wieso der GCC die Abhängigkeit von der GMP hat, damit er das machen kann. Vielleicht haben sie es auch mittlerweile angepasst, so dass der GCC da strikter dem Standard folgt. Ich teste solche Dinge schließlich nicht routinemäßig alle paar Jahre. Jedenfalls gab es da mindestens in der prä-C++11 und prä-SSE Ära Kombinationen, wo unerwartete Ergebnisse rauskamen, sowohl wegen 80Bit 387-Registern, als auch wegen Compiletimeauswertungen.
-
@Swordfish sagte in Rechenprogramm liefert "machmal" falsche Werte:
Du möchtest mir sagen, daß es Compiler gibt (nein, nicht mit irgendwelchen Switches) bei denen in
float foo = 0.42 * 21;
dasfoo
plötzlich double precision hat!?Ja, ein real existierendes Beispiel dafür war die Cray XMP.
Alle zurzeit extierenden x86 CPUs haben auch weiterhin 80Bit x87 FPUs verbaut. Im 64Bit Modus wird aber üblicherweise bei den kein Code mehr für die x87 FPU erzeugt, weil das sehr langsam wäre. Es wird statt dessen SSE oder AVX für Floatingpoint genutzt, und diese Einheiten können nur 64Bit Floats. Oftmals wird dafür nun bei long double eine Softwareemulation für quadruble precision genutzt.
Auf einer HP-PA Maschine ist long double immer quadruple precision, weil die HP-PAs die bisher einzigen existierenden CPUs waren, die quad precision in Hardware konnten.
IBMs POWER9 CPU kann dafür dezimal Floatingpoint in Hardware.
-
@SeppJ sagte in Rechenprogramm liefert "machmal" falsche Werte:
Aber das was hustbaer sagte gilt: Wenn Compiler Ausdrücke zur Compilerzeit auswerten, werden sie dabei oft unendliche Präzision benutzen. Auch wenn das nicht strikt standardkonform ist.
Das. Und genau deshalb habe ich oben geschrieben "ohne irgendwelche Switches", also Standard. IMHO muss
float foo = ...
einfloat foo
bleiben.
-
@DirkB sagte in Rechenprogramm liefert "machmal" falsche Werte:
@SeppJ Darf der Compiler
int main(int argc, char *argv[]) { int a = 26, b =27; float hs; hs = a * b * 365.25 * 24 * 60; printf("Ergebnis HS: %f\n", hs); // printf("Ergebnis aus Printf: %f\n", a * b * 365.25 * 24 * 60); return 0; }
durch
int main(int argc, char *argv[]) { puts("369223936.000000"); }
ersetzen ?
oder schreibt er369223920.000000
(den double Wert)?Ja, darf. As if. Aber das ist eben genau der Punkt den ich bezweifle. IMHO wäre bei der Optimierung trotzdem nach
float
zu casten bevor das irgendwie ein String wird und ausgegeben.Weil:
A prvalue of floating-point type can be converted to a prvalue of another floating-point type. If the source value can be exactly represented in the destination type, the result of the conversion is that exact representation. If the source value is between two adjacent destination values, the result of the conversion is an implementation-defined choice of either of those values. Otherwise, the behavior is undefined.
und
[...] If the scaled value is in the range of representable values for its type, the result is the scaled value if representable, else the larger or smaller representable value nearest the scaled value, chosen in an implementation-defined manner. The type of a floating literal is double unless explicitly specified by a suffix. The suffixes f and F specify float, the suffixes l and L specify long double. If the scaled value is not in the range of representable values for its type, the program is ill-formed.
Bzgl.
FLT_EVAL_METHOD
. Aber am Ende muss sich das ergebnis wieder in einen bestimmten Typ zwängen.
-
@john-0 sagte in Rechenprogramm liefert "machmal" falsche Werte:
@Swordfish sagte in Rechenprogramm liefert "machmal" falsche Werte:
Du möchtest mir sagen, daß es Compiler gibt (nein, nicht mit irgendwelchen Switches) bei denen in
float foo = 0.42 * 21;
dasfoo
plötzlich double precision hat!?Ja, ein real existierendes Beispiel dafür war die Cray XMP.
Ja, nee. Schon C++11 aufwärts.
-
@Swordfish sagte in Rechenprogramm liefert "machmal" falsche Werte:
@SeppJ sagte in Rechenprogramm liefert "machmal" falsche Werte:
Aber das was hustbaer sagte gilt: Wenn Compiler Ausdrücke zur Compilerzeit auswerten, werden sie dabei oft unendliche Präzision benutzen. Auch wenn das nicht strikt standardkonform ist.
Das. Und genau deshalb habe ich oben geschrieben "ohne irgendwelche Switches", also Standard.
Ich meine halt, dass das "Standard"verhalten üblicher Compiler eben nicht das des Sprachstandards ist, sondern dass man das Sprachstandardverhalten durch Switches erzwingen muss. Siehe z.B. GCC-Switches
-fexcess-precision
und-ffloat-store
. Die sind ja für irgendwas da. Wobei es mir, wie gesagt, noch nicht gelungen ist, ein Beispiel zu produzieren. Vielleicht halten sie sich neuerdings strikter an den Standard, auch ohne Switches. Oder meine Hardware ist so beschaffen, dass auch die optimierte Rechnung zu genau dem gleichen Ergebnis führt wir mitffloat-store
. Oder ich bin zu dumm, ein künstliches Beispiel zu produzieren.
-
Wer ist immer noch nicht glaubt, man kann den IEEE Status mittlerweile in C abfragen. Das Ergebnis ist eindeutig.
#include <stdio.h> #include <stdlib.h> #include <fenv.h> // GCC mag das pragma nicht ggf. aktivieren //#pragma STDC FENV_ACCESS on int main(int argc, char *argv[]) { int a, b, fex; double dhs; float fhs; printf("Give a: "); scanf("%i", &a); printf("Give b: "); scanf("%i", &b); dhs = a * b * 24 * 60 * 365.25; fex = fetestexcept (FE_INEXACT); feclearexcept(FE_ALL_EXCEPT); printf ("Ergebnis dhs: %f\n", dhs); if (FE_INEXACT == fex) { printf ("fenv status FE_INEXACT\n"); } else if (0 == fex) { printf ("fenv status clean\n"); } fhs = a * b * 24 * 60 * 365.25; fex = fetestexcept(FE_INEXACT); feclearexcept(FE_ALL_EXCEPT); printf ("Ergebnis fhs: %f\n", fhs); if (FE_INEXACT == fex) { printf ("fenv status FE_INEXACT\n"); } else if (0 == fex) { printf ("fenv status clean\n"); } return EXIT_SUCCESS; }
-
@SeppJ sagte in Rechenprogramm liefert "machmal" falsche Werte:
Ich bin mir nicht sicher, ob Sarkast das richtige meint, je öfter ich es lese, desto eher denke ich, das nicht. Aber das was hustbaer sagte gilt: Wenn Compiler Ausdrücke zur Compilerzeit auswerten, werden sie dabei oft unendliche Präzision benutzen. Auch wenn das nicht strikt standardkonform ist.
Wenn ich schreibe, daß hustbaer es so verstanden hat, wie ich es meine, wie sollte ich es dann anders meinen?
Weil hier eh nur C steht, hab' ich das einmal durch nen ollen Whatcom C/C++ geschoben und den puren Pelles C, gleiches Ergebnis. Ob Standard oder nicht, die zwei Compiler arbeiten durchgängig mit double, wenn es nur ums Zusammenramschen der Literale zur Compiletime geht. Es ist halt so.