Rechenprogramm liefert "machmal" falsche Werte



  • Ok. Ich rudere zurück. Gaaaanz fest. 369223920 ist representable. Aber Deine Vorstellung vom Typsystem ist totzdem schräg. Da wird nichts "konvertiert". printf() kann gar keine floats nehmen. operator<<() für ostreams erst recht nicht wenn das gefütterte double ist. Und abgeschnitten wird garnichts (bei der Übergabe an betreffende Funktion).

    Die besseretm Antwort:

    Bei der Zeile

    // float hs;  // ein hoch auf two-letter-identifiers
    hs = a * b * 365.25 * 24 * 60;
    

    findet implicit ein cast von double in float statt.

    Bei

    printf("Ergebnis aus Printf: %f\n", a * b * 365.25 * 24 * 60);
    

    bleibt es ein double.



  • @Swordfish sagte in Rechenprogramm liefert "machmal" falsche Werte:

    Ok. Ich rudere zurück. Haaaanz fest. 369223920 ist representable. Aber Deine Vorstellung vom Typsystem ist totzdem schräg. Da wird nichts "konvertiert". printf() kann gar keine floats nehmen. operator<<() für ostreams erst recht nicht. Und abgeschnitten wird garnichts (bei der übergabe an betreffende Funktion).

    Rudern wir beide zurück.
    Also, wir haben den Fakt, daß ich das Ergebnis reproduzieren kann, aber nur sehr, sehr schlicht erklären. So, I'm a simple C- Guy. Halt mal den Kopf schief, ich werd dich nicht ansteigen, aber irgendwas passiert mit den 0,25 und warum und wieso vertage ich für mich auf morgen, was ja schon wieder heute ist. Alles cool?



  • Mit den 0.25 passiert garnichts aufregendes. Alles andere wird zu double.

    Alles easy bro. Aber den Grund für die Ausgabe habe ich oben genannt. Einmal ist es float der bei Übergabe an printf() in ein double gestopft wird und bei der direkten Berechnung als Parameter für printf() ein double.

    Die Nächstmögliche Repräsentation von 369.223.920 als float (auf üblichen Architekturen, IEEE-sonstwas mit 16 bit) ist 369.223.936



  • @Swordfish ich bin seit 5:00 gestern auf, ich bin alle. Morgen nörgeln wir weiter 😉



  • @Sarkast Siehe edit.

    @Sarkast sagte in Rechenprogramm liefert "machmal" falsche Werte:

    ich bin seit 5:00 gestern auf

    Sportlich.



  • Das liefert aber gar keine falsche Werte.



  • @Sarkast sagte in Rechenprogramm liefert "machmal" falsche Werte:

    @Swordfish ich bin seit 5:00 gestern auf, ich bin alle. Morgen nörgeln wir weiter 😉

    Dann lies aber vorher nochmal alle Antworten durch.



  • @DirkB Meinst Du mich oder @Sarkast?



  • @Swordfish sagte in Rechenprogramm liefert "machmal" falsche Werte:

    @DirkB Meinst Du mich oder @Sarkast?

    Den Unausgeschlafenen (Sarkast).

    Ich habe ihn zitiert, wo er dich angesprochen hat.



  • @DirkB Aso. Sorry.



  • @DirkB Na, da hatte Swordfish wohl ein wenig @edit gespielt oder ich war zu platt, noch alles nachzuvollziehen. Ich muß so früh raus, um meinen Bus zu erwischen. Aber es sieht ganz sinnvoll aus, was da steht. 👍
    Ich werd nochma gucken, was aus dem Compilat hervorgeht, aber zuerst Mittagessen, dann Rasen stutzen, dann ggf nörgeln.



  • @Sarkast sagte in Rechenprogramm liefert "machmal" falsche Werte:

    Na, da hatte Swordfish wohl ein wenig @edit gespielt oder ich war zu platt, noch alles nachzuvollziehen.

    Das wWesentliche habe ich gestern ziemlich Zzeitnah editiert.

    // edit: pun intended.



  • Nimm für hs ein double da prinf float in double convertiert weshalb mit einem float 16 mehr Rauskommen keine Ahnung.



  • @Abe sagte in Rechenprogramm liefert "machmal" falsche Werte:

    weshalb mit einem float 16 mehr Rauskommen keine Ahnung.

    Weil das da. Du hättest aber einfach auch die bisherigen Beiträge lesen können.



  • @Swordfish danke hab schon gelesen das ihr das erwähnt hab. Hab halt noch nie von gehört und es in der Sekunde versucht getrost zu ignorieren.


  • Mod

    Nimm mal an, ein float hätte nur 3 Bit. Dann kann ich damit 8 Werte darstellen. Beispielsweise 0/8, 1/8, 2/8, 3/8, ... oder in Dezimalschreibweise: 0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875. Das ist effektiv weniger als eine Dezimalstelle an Genauigkeit, denn ich könnte dir 0, 0.1, 0.2, 0.3, 0.5, 0.6, 0.7, oder 0.8 sagen, und du wüsstest jeweils exakt, welche der darstellbaren Zahlen ich damit meine (und 0.4 und 0.9 fehlen sogar). Trotzdem hat die vollständige Darstellung dieser Zahlen im Dezimalsystem bis zu 3 Nachkommastellen. Diesbezüglich ist es egal, dass man nur eine zählende Stelle hat (bzw. sogar ein bisschen weniger), die Darstellung interessiert sich dafür nicht.

    Dabei ist der Transfer vom Binärsystem ins Dezimalsystem noch halbwegs gutartig. Angenommen der float würde intern nicht binär sondern mit einem 3-wertigen Tri-Bit arbeiten. Dann hätte ein float mit 2 solcher Tri-Bits 9 mögliche Zustände 0/9, 1/9, 2/9, und so weiter. Das sind effektiv immer noch weniger als eine Dezimalstelle, aber wenn man die Zahlen im Dezimalsystem vollständig ausschreiben wollte, bräuchte jeder dieser Werte (außer 0) unendlich viele Stellen!

    Genauigkeit != Anzahl der Stellen der Darstellung

    Dass man alle Binärzahlen im Dezimalsystem exakt darstellen kann, liegt übrigens an den Primfaktoren. Die Basis des Binärsystems (2) ist vollständig in der Basis des Dezimalsystems (10=2*5) enthalten. Die Basis des Dreiersystems (3) hingegen gar nicht, daher sind alle Werte des Dreiersystems in Dezimalschreibweise so krumm (außer die, wo sich die 3 wegkürzt, also die Ganzzahlen). Und auch umgekehrt von Dezimal zu Binär fehlt die 5, daher ist 0.1 = 1/(2*5) nicht exakt im Binärsystem darstellbar (Häufiger Fallstrick beim Programmieren! Initialisiert man Fließkommawerte mit 0.1 oder ähnlichem, dann ist das gar nicht genau 0.1!), sondern nur so Werte wie 0.5 = 5/(2*5) = 1/2 oder 0.75 = 75 / (2*5)^2 = 3/2^2, wo sich die 5 heraus kürzt.



  • @SeppJ Also, habs mir nochma angeguckt. Deine Darlegungen sind zwar absolut richtig, erklären aber das Phänomen unzureichend. Der springende Punkt ist, daß float tatsächlich zuwenig signifikante Dezimale abbildet.
    Wenn man statt der scanf()- Einleserei a und b entsprechend vorbelegt, hat man nur noch konstante Ausdrücke, da erledigt der Compiler die Rechnerei vorab in double- precision. Keine Abweichung.
    Legt man hs als double an, ist der Spuk ebenfalls aus der Bude gebannt, double reicht aus. Auch keine Abweichung.
    Als float angelegt, wird bei live- Werten hs zu unpräzise errechnet, da aber bei printf für %f der Zieltyp tatsächlich double ist, haben wir für die eigentliche Operation zwei unterschiedliche Zieltypen, für hs eben float (erst für die Ausgabe in double gewandelt), für printf direkt ist es halt schon double. Bedeutet Abweichung.

    War das jetzt zu faselig oder kammas verstehn? Was auch noch recht interessant sein könnte, nachdem die Bitbreiten in C/++ relativ unverbindlich sind, gibt es wenigstens für float/double was Verbindliches? Hab da wenig Ahnung.

    PS: Eigentlich hätte ich mich an eine Workshopreihe an der Uni erinnern müssen, einer der erstaunlichsten war "In der Floating- Point- Falle"



  • @Sarkast sagte in Rechenprogramm liefert "machmal" falsche Werte:

    Wenn man statt der scanf()- Einleserei a und b entsprechend vorbelegt, hat man nur noch konstante Ausdrücke, da erledigt der Compiler die Rechnerei vorab in double- precision. Keine Abweichung.

    Das ist völlig wurscht. Ob das jetzt zur Compiletime passiert oder zur Runtime. In dem ganzen Ausdruck ist mindestens ein double drin, also wird auch mit double gerechnet.

    Sonst genau das was ich hier schon gesagt habe. [expr.call/12] und [conv.fpprom] (schimpft sich default argument promotion).

    @Sarkast sagte in Rechenprogramm liefert "machmal" falsche Werte:

    gibt es wenigstens für float/double was Verbindliches?

    Von IEEE 754 (IEC 559) kannst Du ausgehen. Soll sogar angeblich bald vom Standart vorgeschrieben werden (P1467R1).
    std::numeric_limits<>::is_iec559() verrät es Dir.

    Früher mal haben x86 CPUs in 80 bit gerechnet (das waren long double), heute sind es 32 (float) und 64 (double) bit.

    Der Standart verlang von long double nur daß er mindestens so breit ist wie double ([basic.fundamental]/12).

    // final edit: done.



  • @Swordfish sagte in Rechenprogramm liefert "machmal" falsche Werte:

    Das ist völlig wurscht. Ob das jetzt zur Compiletime passiert oder zur Runtime. In dem ganzen Ausdruck ist mindestens ein double drin, also wird auch mit double gerechnet.

    Nee, ehmts ja nicht. Hab ein wenig Spielwiese gemacht:

    #define GETVALUES
    // #define MAKEDOUBLE
    int main(int argc, char *argv[])
    {
    //    printf("Hello, world!\n");
    int a, b;
    #ifdef MAKEDOUBLE
    double hs;
    #else
    float hs;
    #endif
    #ifdef GETVALUES
    printf("Give a: ");
    scanf("%i", &a);
    #else
       a = 26; printf(" %i\n",a);
    #endif
    
    #ifdef GETVALUES
    printf("Give b: ");
    scanf("%i", &b);
    #else
       b = 27; printf(" %i\n",b);
    #endif
    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;
    }
    

    Das hab ich gemeint, float wird nur bei der Runtime wirksam, auch wenn's für printf nachher in double gewandelt wird.

    @Swordfish sagte in Rechenprogramm liefert "machmal" falsche Werte:

    Sonst genau das was ich hier schon gesagt habe. [expr.call/12] und [conv.fpprom] (schimpft sich default argument promotion).

    @Sarkast sagte in Rechenprogramm liefert "machmal" falsche Werte:

    gibt es wenigstens für float/double was Verbindliches?

    Der Standart verlang von long double nur daß er mindestens so breit ist wie double ([basic.fundamental]/12).

    // final edit: done.
    Ah, OK, das wußte ich noch nicht! 👍



  • @Sarkast sagte in Rechenprogramm liefert "machmal" falsche Werte:

    Das hab ich gemeint, float wird nur bei der Runtime wirksam, auch wenn's für printf nachher in double gewandelt wird.

    Keine Ahnung was Du sagen willst.

    hs   =   a * b * 365.25 * 24 * 60  ;
    //   ^   ^^^^^^^^^^^^^^^^^^^^^^^^
    //   |   DER Ausdruck ist double, egal ob a und b mit scanf() gelesen werden oder nicht und auch egal welchen Typ hs hat.
    //   +--- da findet eine Konvertierung von double zu float statt wenn hs float ist (= mist)
    

    Das ist so. Egal ob das zur Compiletime oder zur Runtime gerechnet wird.


Anmelden zum Antworten