printf float/double unterschied in Vorkommastellen? Linux/Windows



  • Folgender Code liefert unter Linux/Windows unterschiedliche Zahlen in den Vorkommastellen - die GLIBC rechnet wenn ich das richtig gelesen habe mit BigNums - ist das der Grund?

    #include <float.h>
    #include <stdio.h>
    
    int main(int argc, char** argv)
    {
      printf("[0] %f\n", FLT_MAX);
      printf("[1] %lf\n", FLT_MAX);
      printf("[2] %Lf\n", FLT_MAX); // gcc warning: expects argument of type ‘long double’
      printf("[3] %f\n", DBL_MAX);
      printf("[4] %lf\n", DBL_MAX);
      printf("[5] %Lf\n", DBL_MAX); // gcc warning: expects argument of type ‘long double’
    
      //mit C++ und std::numeric_limits<float/double>::max() kommt das selbe raus
    
      return 0;
    }
    
    unter Ubunut:
    x64
    lsb_release -d prints "Description:	Ubuntu 15.04"
    gcc --version prints "gcc (Ubuntu 4.9.2-10ubuntu13) 4.9.2"
    ldd --version prints "ldd (Ubuntu GLIBC 2.21-0ubuntu4) 2.21"
    
    [0] 340282346638528859811704183484516925440.000000
    [1] 340282346638528859811704183484516925440.000000
    [2] --> warning-Zeile aukommentiert
    [3] 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000
    [4] 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000
    [5] --> warning-Zeile aukommentiert
    
    unter Windows 7 x64
    VS2010 (latest Version 10.0.40219.1 SP1Rel) Debug/Win32
    
    [0] 340282346638528860000000000000000000000.000000
    [1] 340282346638528860000000000000000000000.000000
    [2] 340282346638528860000000000000000000000.000000
    [3] 179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000000
    [4] 179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000000
    [5] 179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000000
    

    kann mich bitte jemand erleuchten?



  • der Unterschied zwischen

    float32_max
    VS2010 340282346638528860000000000000000000000.000000
    GCC4.9.2 340282346638528859811704183484516925440.000000

    von 1.8829581651548307456e+20 ist ja auch nicht gerade wenig, wie gesagt Vorkommastellen

    bei 64Bit ist das noch viel schlimmer

    oder fehlt mir unter VS2010 nur die richtige Formatierung "l" und "L" habe
    ich ja schon probiert



  • Float als auch Double haben nur eine bestimmte Genauigkeit von 7-8 bzw. 15-16 Dezimalstellen, s. IEEE 754
    Alle Ziffern dahinter sind reines Rauschen...

    Bei deiner Ausgabe sind es ja 16 gleiche Stellen. Du mußt auch wissen, daß bei printf die Parameter stets auf double erweitert werden (bzw. auf int bei Ganzzahlen). Daher kommt bei [0] und [1] sowie [3] und [4] jeweils dieselbe Zeichenfolge raus 😉



  • und weil die GLIBC intern mit BigNums arbeitet rauscht es nach den 16 Stellen eben ein bisschen anders als beim VS2010-printf - richtig so?

    gibt es bei printf eine Möglichkeit die Ausgabe bei beiden gleich zu haben
    z.B. mit %e oder ist das alles zu implementationsabhängig d.h. ich muss eben dafür
    sorgen das meine Plattformen den gleichen Formatierungscode nutzen



  • Gast3 schrieb:

    .. oder ist das alles zu implementationsabhängig

    Du kannst ja noch nicht mal davon ausgehen, dass FLT_MAX bzw. DBL_MAX überall gleich sind.



  • Du kannst auch mit %e und den sinnvollen Stellen arbeiten:

    printf("%.*e", DBL_DIG-1 , DBL_MAX);
    


  • @DirkB

    Du kannst ja noch nicht mal davon ausgehen, dass FLT_MAX bzw. DBL_MAX überall gleich sind

    hast du zufällig ein Beispiel



  • Sei nicht so faul. Alle Hinweise sind gegeben.

    Gast3 schrieb:

    und weil die GLIBC intern mit BigNums arbeitet rauscht es nach den 16 Stellen eben ein bisschen anders als beim VS2010-printf - richtig so?

    Nur weil du irgendwo was aufgeschnappt hast, musst du nicht denken, dass du es auch verstanden hast und irgendwelche Schlussfolgerungen ziehen.



  • Sei nicht so faul. Alle Hinweise sind gegeben.

    Nur weil du irgendwo was aufgeschnappt hast, musst du nicht denken, dass du es auch verstanden hast und irgendwelche Schlussfolgerungen ziehen.

    was meinst du? - klar prüfe ich mein Linux/Windows/VxWorks/Solaris/ARM System wie da die Implementierungen arbeiten

    keine Ahnung was du mir sagen möchtest - ich habe die VS2010,VS2008 printf float/double implementationen mit aktuellen GLIBC und ulib verglichen - was sollte ich den deiner Meinung anderes aus den Quellen Schlussfolgern - oder falls ich das deiner Meinung nicht machen soll - was dann?

    Ich habe keine Ahnung was du mir sagen willst - ich würde jetzt "einfach" eine Gleichschaltung des Codes auf meinen Platformen machen - oder was sonst?



  • Ich will dir damit sagen, dass du nichts verstanden hast, sondern bloß nachplapperst, was du irgendwo aufgeschnappt hast.
    Die hier gegebenen Hinweise, z.B. über die default argument promotion ignorierst du, und plapperst weiter nach.
    Der C Standard schreibt keinerlei Implementierungen vor, außerdem wird bei dir nichts gerechnet und Bignums dienen zum Rechnen und nicht zur Datenhaltung.
    Außerdem ist ein Vorteil von Bignums eben gerade, dass sie nicht "rauschen", auch dabei warst du zu faul, zu lernen.
    Du kannst oder willst nicht lernen.
    Ich will dir damit sagen, dass du nichts verstanden hast.



  • Ich will dir damit sagen, dass du nichts verstanden hast, sondern bloß nachplapperst, was du irgendwo aufgeschnappt hast.

    Wutzgeschnatter oder?
    ich habe es im GLIBC Quelltext gelesen - nicht aufgeschnappt

    Die hier gegebenen Hinweise, z.B. über die default argument promotion ignorierst du, und plapperst weiter nach.

    Welche Bedeutung hat das hier? float auf double promoted änder doch im meinen Szenario nicht wirklich was

    Der C Standard schreibt keinerlei Implementierungen vor, außerdem wird bei dir nichts gerechnet und Bignums dienen zum Rechnen und nicht zur Datenhaltung.

    Das der C Standard nichts vorschreibt ist mir jetzt klar - aber GLIBC nutzt das eben so für die printf float/double Formatierung (deswegen meine Aussage) - weil einfaches skalieren/mod eben auch nicht per se die Lösung ist

    Du kannst oder willst nicht lernen.
    Ich will dir damit sagen, dass du nichts verstanden hast.

    Ich habe eine Applikation die Client(mit String-Ausgabe) oder Server (holt nur Daten) spielt und auf x Plattformen (x86 (Linux/Windows),ARM (Linux, WinCE),Sparc (Linux/Solaris), VxWorks) läuft - also habe ich jetzt die Erkenntnis das ich ohne einen Nicht-C-Standard Konverter für float/double -> string keine homogene Ausgabe über die Plattformen hinweg erreichen kann - Th69 (3. Post) Antwort hat das aber schon völlig geklärt

    deine leicht frechen und hinterhergedrückten Kommentare sind fast wertfrei - aber wenn es dich freut bin ich auch glücklich



  • um es also auf den Punkt zu bringen

    kennt jemand eine gute C/C++ Library um IEEE 754 Konforme Singles/Doubles

    unter diesen Platformen (soweit ich weiss alle IEEE 754 Konform)

    x86 (Linux/Windows)
    ARM (Linux, WinCE)
    Sparc (Linux/Solaris)
    VxWorks

    mind. in VS2010-printf "Qualität" und Nicht-Exponentialdarstellung in einen String umzuwandeln (mit Nachkommastellen-Angabe) und das auch noch schnell 🙂

    meine bisherigen Kandidaten sind:

    https://github.com/client9/stringencoders arbeite mit Hochskalieren - also begrenzt und mit teilweise starken Rundungsfehlern (im vergleich zu VS2010 printf)

    https://github.com/google/double-conversion

    https://raw.githubusercontent.com/miloyip/dtoa-benchmark/master/src/milo/dtoa_milo.h wechselt leider relativ schnell auf Exponentialdarstellung

    ansonsten hatte ich noch die Idee ins .Net-Backend zu schauen

    Ideen?



  • @Wutz: Mit Deinem Verständnis ist es aber auch nicht weit her. Mit "default argument promotion" hat die Frage des OP nichts zu tun.

    @Gast3:

    DBL_MAX ist definiert in der float.h:

    In VisualStudio:

    #define DBL_MAX         1.7976931348623158e+308 /* max value */
    

    In meinem Debian Wheezy (wahrscheinlich genauso in Deinem Ubuntu):

    #define DBL_MAX		__DBL_MAX__
    

    wobei __DBL_MAX__ in Debian-GCC fest einkompiliert ist als

    #define __DBL_MAX__ ((double)1.79769313486231570815e+308L)
    

    Diese dezimalen Zahlen muss der Compiler in ein Double - also einen 64-bit-Wert - umrechnen.

    Die größte positive Double-Zahl ist 0x7FEFFFFFFFFFFFFF, was ganz "rauschlos" in dezimal darstellbar ist als:

    179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.0
    

    Die nächstkleinere Double-Zahl (0x7FEFFFFFFFFFFFFE) lautet in dezimal:

    179769313486231550856124328384506240234343437157459335924404872448581845754556114388470639943126220321960804027157371570809852884964511743044087662767600909594331927728237078876188760579532563768698654064825262115771015791463983014857704008123419459386245141723703148097529108423358883457665451722744025579520.0
    

    Wenn Du die beiden Zahlen vergleichst, entdeckst Du eine riesige Lücke zwischen der größten und der nächstkleineren Zahl. Dezimale Zahlen, die dazwischen liegen, müssen gerundet werden. Umgekehrt kann man Doubles lediglich als Näherungswerte betrachten, da der ursprüngliche Wert höchstwahrscheinlich anders aussah. Wenn Du viel mit ungenauen Gleitkommazahlen rechnest, dann steigt die Ungenauigkeit (das ist der ursprüngliche Inhalt des Schmetterling-Hurrikan-Gleichnisses).

    Der Compiler rechnet nun 1.79769313486231570815e+308 in 0x7FEFFFFFFFFFFFFF um. 1.79769313486231570815e+308 ist nun exakt

    179769313486231570815000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
    

    Wenn Dein gcc-printf ehrlich wäre, dürfte es nicht 0x7FEFFFFFFFFFFFFF exakt umrechnen, da das Ergebnis erstens nicht stimmt und zweitens eine nicht gegebene Genauigkeit vortäuscht. Das VS-printf ist da besser: wegen der vielen Nullen kannst Du Dir denken, dass das nur ein Näherungswert ist.

    Inwieweit da Big Numbers involviert sind, ist eigentlich uninteressant. Mit Big Numbers kannst Du ein Double exakt in seinen Dezimalwert umwandeln, aber: stimmt das Ergebnis denn eigentlich auch wirklich? Andere Berechnungen sind schneller, wenn man davon ausgeht, dass der ganze Rattenschwanz wegen seiner Ungenauigkeit sowieso egal ist.

    Auf Singles lässt sich das Ganze analog anwenden.

    viele grüße
    ralph



  • Wenn Dein gcc-printf ehrlich wäre, dürfte es nicht 0x7FEFFFFFFFFFFFFF exakt umrechnen, da das Ergebnis erstens nicht stimmt und zweitens eine nicht gegebene Genauigkeit vortäuscht. Das VS-printf ist da besser: wegen der vielen Nullen kannst Du Dir denken, dass das nur ein Näherungswert ist.

    das hatte mich bei meinen Vergleichen stark verwundert - daher eigentlich die Frage, aber vielen Dank für die bessere Aufklärung

    ich denke ich werde mir die Implentationen von _fcvt in VS2010-runtime, newlib und uclib und anderen mal anschauen und hoffe den passenden Konverter für alle meine Plattformen zu finden

    Inwieweit da Big Numbers involviert sind, ist eigentlich uninteressant. Mit Big Numbers kannst Du ein Double exakt in seinen Dezimalwert umwandeln, aber: stimmt das Ergebnis denn eigentlich auch wirklich? Andere Berechnungen sind schneller, wenn man davon ausgeht, dass der ganze Rattenschwanz wegen seiner Ungenauigkeit sowieso egal ist.

    ich bin mir da auch nicht sicher - aber es werden eben Teile mit GMP gerechnet
    https://sourceware.org/git/?p=glibc.git;a=blob;f=stdio-common/printf_fp.c

    und ja der Benchmark-König ist GLIBC-printf nicht wirklich - die VS2010-runtime ist da schon einiges schneller



  • rkhb schrieb:

    @Wutz: Mit Deinem Verständnis ist es aber auch nicht weit her. Mit "default argument promotion" hat die Frage des OP nichts zu tun.

    Du hast keine Ahnung wovon du redest.


Log in to reply