C- double Genauigkeit



  • Hallo,

    ich wollte fragen was bedeutet es das double 15 stellen Genauigkeit.

    Ich habe folgendes Programm geschrieben

    auto double d = 1.11 ;
    auto double d2 = 2.11 ;
    auto double d3 ;
    
    int i ;
    
    for ( i = 1 ; i < 4 ; i++ )
    {
    	d *= d ;
    	d2 *= d2 ;
    	d3 = d * d2 ;
    }
    
    printf ( "%.15f\n" , d3 ) ;
    

    also an sich macht der

    (1.11 ^ 2 ) * (2.11 ^ 2 ) = 5.48543241 richtiger wert
    5.485432410000001 programmausgabe
    (1.11 ^ 4 ) * (2.11 ^ 4 ) = 30.0899687246784081
    30.089968724678410
    (1.11 ^ 8 ) * (2.11 ^ 8 ) = 905.4062178521247452
    905.406217852125000

    Allgemein möchte ich programme schreiben die mit der höchstmöglichen (ich verzichte jedoch auf long double und entscheide mich für double) genauigkeit bei wahl von double
    mit gleitkommazahlen rechnet und ausgibt

    Im Internet

    http://openbook.rheinwerk-verlag.de/c_von_a_bis_z/005_c_basisdatentypen_008.htm#mj357b36b759cbb526c1701f1341d99a96

    habe ich 15 stellen für double gefunden.
    Also ich weiß was signifikante Stellen sind.

    Kann mir jemand obige ausgaben erklären?
    Warum ist beim obersten wert eine 1 am ende?
    Warum ist der bei der zweiten Ausgabe 16 signifkante stellen richtig (die letzte wurde richtig gerundet)
    und bei der letzten ausgabe nur 15 stellen richtig (hier wurde die 15 stelle richtig gerundet und nicht die 16.te)?

    VIelen Dank
    James



  • Heißt 15 stellen genauigkeit dass ich auf jeden 15 korrekt stellen evt. gerundet bekomme

    und nur in speziellen fällen auch mal eine stelle mehr richtig bekomme?



  • @JamesNguyen Das bedeutet, dass eine Zahl auf die ersten 15 (16) Stellen gerundet genau ist.
    Danach kommen Ungenauigkeiten.

    Diese Fehler können sich bei Berechnungen vergrößern.

    Auf https://www.h-schmidt.net/FloatConverter/IEEE754de.html kannst du mit float rum spielen.
    Z.B 0.1 bekommst du nicht genau hin



  • Danke

    Heißt das also, dass ich mehr als 15 stellen bekommen kann aber diese immer falsch sind?



  • und wenn das so ist

    wie kann ich das in der Implementierung berücksichtigen?

    Also wie kann ich machen, dass er bei allen double werten alles nach der 15. (sign) Stelle wegschneidet

    und das halt immer sobald er einen neuen Wert berechnet hat?



  • und wieso hat man das in C nicht gleich so konzipiert?

    und kannst du ein bsp. geben in dem sich ein fehler bei einer berechnung vergrößert?



  • @JamesNguyen sagte in C- double Genauigkeit:

    und kannst du ein bsp. geben in dem sich ein fehler bei einer berechnung vergrößert?

    for(double x=0.0;x<100.0;x=x+0.1)
      printf("%.16f\n", x);
    

    (ungetestet, evtl musst du die 100 verändern)



  • ok, ok

    das habe ich glaube ich tatsächlich so einmal gehört

    ich glaube da hat man uns gesagt bspw.

    statt bspw.

    0.1 + 0.1 +0.1 zu machen

    lieber

    1 + 1 + 1 und / 3

    zu machen

    aber wieso es zu diesen ausgaben kommt versteh ich gar nicht also was hat der da intern überhaut gerechnet das da sowas
    rauskommt.

    Wie geht man in C jetzt damit um ? wie korrigiert man das im Beispiel? und wie hilft einem überhaupt das Wissen um
    die 15. stellen genauigkeit dabei?



  • @JamesNguyen sagte in C- double Genauigkeit:

    aber wieso es zu diesen ausgaben kommt versteh ich gar nicht also was hat der da intern überhaut gerechnet das da sowas
    rauskommt.

    Es gibt genug Beiträge zu IEEE 754. Ich hatte schon ein paar Links angegeben.

    Wie geht man in C jetzt damit um ? wie korrigiert man das im Beispiel?
    Es gibt Extra Bibliotheken für große Zahlen und/oder Genauigkeit

    und wie hilft einem überhaupt das Wissen um
    die 15. stellen genauigkeit dabei?

    Z.B. dass man Geldbeträge nicht als Fließkommazahl speichert - bei den Ungenauigkeiten versteht keiner Spaß.

    Oder wenn du sehr viele Werte mit einem großem Wertebereich hast, musst du dir geeignete Maßnahmen überlegen, damit alle Werte vernünftig berüchsichtigt werden.

    „Wie lang ist die Küste von Großbritannien?“



  • genau ok

    also bei Geldbeträgen

    weiß ich ja dass sie zwei Nachkommstellen haben

    in solchen fällen

    würde ich dann

    den cent betrag speichern und dann addieren

    und am ende durch 100 teilen

    aber was wenn ich nicht weiß wie viele nachkommstellen etwas hat dann kann ich diese methode ja nicht machen



  • @JamesNguyen sagte in C- double Genauigkeit:

    den cent betrag speichern und dann addieren

    Aber als Ganzzahltyp

    und am ende durch 100 teilen

    Nur für die Ausgabe

    aber was wenn ich nicht weiß wie viele nachkommstellen etwas hat dann kann ich diese methode ja nicht machen

    Das hängt von der Anwendung ab.
    Welche Genauigkeit reicht dafür?
    Kann man das in Teilaufgaben zerlegen?

    Wo brauchst du diese hohe Genauigkeit?

    Dafür gibt es Informatiker



  • so also wenn ich bsp. 33,10€ eintragen will. ich sehe das geld also nur (es ist also - genau das wollen wir ja nicht -
    im computer als gleitkomma gespeichert.)

    ich sehe das geld und kann mit oder ohne taschen rechner

    • 100 machen

    und bekomme

    int g = 3310 ;

    für die mache ich dann bspw.

    int euro = g / 100; schneidet mir die cents ab (zumindest kenne ich das aus java so)

    int cent = g % 100 ; gibt mir cent

    und dann ausgeben

    nun
    ok also ich habe meinen
    beliebigen double radikaden - ich weiß also schonmal gar nicht wie viele nachkommastellen der hat ist ja beliebig
    also 3.5 oder 0.0025
    weiß also gar nicht ob mal 10 oder mal 10000 um den in eine ganzzahl zu machen

    und den bekomme ich ja schon als double wenn ich 0.1 übergeben bekomme habe ich ja gleich probleme

    weiter soll der durch das wurzelziehen durchiterriert werden

    xN ist beliebiger positiver startewert

    xNplus1 = 1 / ( 2 * xN ) * ( xN * xN + rad ) ;

    selbst wenn ich den radikanden wüsste und in eine ganzzahl machen konnte

    kann und sollte ja

    xNplus1 in irgendeinem schritt eine kommzahl sein

    und dann wird wieder damit weitergerechnet, was wir ja nicht wollen - mit gleitkommas rechnen

    wenn wir nun wieder daraus eine ganz zahl machen wollte müssten wir wieder wissen mit was multiplizieren..

    und dann müssen wir

    am ende ja wieder durch alles teilen was wir an 10nern multipliziert haben



  • wieso ist rechnen mit gleitkomma überhaupt erlaubt in C?

    Gerade weiß ich gar nicht wie man das in C korrekt macht.



  • weil in dem beispiel mit 0.1 kommt

    0.0000000000000000
    0.1000000000000000
    0.2000000000000000
    0.3000000000000000
    0.4000000000000000
    0.5000000000000000
    0.6000000000000000
    0.7000000000000000
    0.7999999999999999
    0.8999999999999999

    raus da hilft mir die angabe von 15 stellen ja irgendwie gar nicht.

    Er rechnet einfach falsch da kann man nur sagen - anders machen



  • @JamesNguyen
    Das hängt von der Anwendung ab.

    Alles als Ganzzahl zu verrechnen ist nicht die Lösung, zumal auch long long nicht an den Wertebereich von double ran kommt.

    Meist ist es auch eine Abwägung zwischen Genauigkeit und Geschwindigkeit.





  • @Swordfish sagte in C- double Genauigkeit:

    @JamesNguyen sagte in C- double Genauigkeit:

    Er rechnet einfach falsch

    Is floating point math broken?
    What Every Computer Scientist Should Know About Floating-Point Arithmetic

    Danke, dass Du das hier anführst, bei JamesNguyen fehlt so einiges an Basiswissen.

    Ich habe vor einiger Zeit ein C++-Programm geschrieben, welches die wichtigen Zahlen (es sind in C und C++ die gleichen Werte) für einen Compiler ausgibt. Sofern auf der Plattform vorhanden wird die Quadmath Library berücksichtigt, wenn man das Programm mit -DQUADMATH übersetzt und mit der passenden Bibliothek bindet, unter Linux/UNIX ist es die Linker Option -lquadmath.

    #include <iostream>
    #include <iomanip>
    #include <cfloat>
    
    #ifdef QUADMATH
    #include <quadmath.h>
    
    // compile with -DQUADMATH -fext-numeric-literals -lquadmath
    constexpr size_t sz_f128 = sizeof(__float128);
    constexpr size_t F128_MDIG = FLT128_MANT_DIG;
    constexpr size_t F128_DIG = FLT128_DIG;
    constexpr long   F128_MEXP = FLT128_MIN_EXP;
    constexpr long   F128_XEXP = FLT128_MAX_EXP;
    constexpr long   F128_M10P = FLT128_MIN_10_EXP;
    constexpr long   F128_X10P = FLT128_MAX_10_EXP;
    constexpr double F128_EPS = FLT128_EPSILON;
    
    char F128_MIN[13];
    char F128_MAX[13];
    
    int r = quadmath_snprintf (F128_MIN, sizeof(F128_MIN), "%+-#*.3Qe", FLT128_MIN);
    int s = quadmath_snprintf (F128_MAX, sizeof(F128_MAX), "%+-#*.3Qe", FLT128_MAX);
    #else
    constexpr size_t sz_f128 = 0;
    constexpr size_t F128_MDIG = 0;
    constexpr size_t F128_DIG = 0;
    constexpr long   F128_MEXP = 0;
    constexpr long   F128_XEXP = 0;
    constexpr long   F128_M10P = 0;
    constexpr long   F128_X10P = 0;
    constexpr double F128_EPS = 0.0;
    
    char const* const F128_MIN = "   0.000e+00";
    char const* const F128_MAX = "   0.000e+00";
    #endif
    
    using namespace std;
    
    // newline erase color and send \n
    char const* const nll = "\x1b[0m\n";
    char const* const nl4 = "    \x1b[0m\n";
    
    // column with description of data type
    char const* const dsc = "\x1b[0;107;30m";
    char const* const dsd = "\x1b[0;47;30m";
    // data column style 1, 2 and 3
    char const* const ts1 = "\x1b[0;106;30m    ";
    char const* const ts2 = "    \x1b[0;103;30m    ";
    char const* const ts2q = "    \x1b[0;103;30m   ";
    char const* const ts3 = "    \x1b[0;106;30m    ";
    // column description style 1 and 2
    char const* const cd1 = "\x1b[0;46;30m";
    char const* const cd2 = "\x1b[0;43;30m";
    
    
    double const EPS = LDBL_EPSILON;
    
    int main () {
    	cout << scientific;
    	cout << dsc << "FLT_RADIX           " << ts1 << setw(68) << FLT_RADIX << nl4;
    	cout << dsd << "                    " << cd1 << "       float       " << cd2 << "      double       " << cd1 << "    long double    " << cd2 << "     __float128    " << nll;
    	cout << dsc << "             sizeof " << ts1 << setw(11) << sizeof(float)  << ts2 << setw(11) << sizeof(double) << ts3 << setw(11) << sizeof(long double) << ts2 << setw(11) << sz_f128 << nl4;
    	cout << dsc << " binary digits mant " << ts1 << setw(11) << FLT_MANT_DIG   << ts2 << setw(11) << DBL_MANT_DIG   << ts3 << setw(11) << LDBL_MANT_DIG       << ts2 << setw(11) << F128_MDIG << nl4;
    	cout << dsc << "decimal digits mant " << ts1 << setw(11) << FLT_DIG        << ts2 << setw(11) << DBL_DIG        << ts3 << setw(11) << LDBL_DIG            << ts2 << setw(11) << F128_DIG << nl4; 
    	cout << dsc << "   minimum exponent " << ts1 << setw(11) << FLT_MIN_EXP    << ts2 << setw(11) << DBL_MIN_EXP    << ts3 << setw(11) << LDBL_MIN_EXP        << ts2 << setw(11) << F128_MEXP << nl4;
    	cout << dsc << "   maximum exponent " << ts1 << setw(11) << FLT_MAX_EXP    << ts2 << setw(11) << DBL_MAX_EXP    << ts3 << setw(11) << LDBL_MAX_EXP        << ts2 << setw(11) << F128_XEXP << nl4;
    	cout << dsc << "minimum 10 exponent " << ts1 << setw(11) << FLT_MIN_10_EXP << ts2 << setw(11) << DBL_MIN_10_EXP << ts3 << setw(11) << LDBL_MIN_10_EXP     << ts2 << setw(11) << F128_M10P << nl4;
    	cout << dsc << "maximum 10 exponent " << ts1 << setw(11) << FLT_MAX_10_EXP << ts2 << setw(11) << DBL_MAX_10_EXP << ts3 << setw(11) << LDBL_MAX_10_EXP     << ts2 << setw(11) << F128_X10P << nl4;
    	cout << dsc << "            minimum " << ts1 << setw(11) << setprecision(3) << FLT_MIN        << ts2 << setw(11) << setprecision(3) << DBL_MIN        << ts3 << setw(11) << LDBL_MIN << ts2q << setw(11) << F128_MIN << nl4;
    	cout << dsc << "            maximum " << ts1 << setw(11) << setprecision(3) << FLT_MAX        << ts2 << setw(11) << setprecision(3) << DBL_MAX        << ts3 << setw(11) << LDBL_MAX << ts2q << setw(11) << F128_MAX << nl4;
    	cout << dsc << "            epsilon " << ts1 << setw(11) << setprecision(3) << FLT_EPSILON    << ts2 << setw(11) << setprecision(3) << DBL_EPSILON    << ts3 << setw(11) << EPS      << ts2 << setw(11) << F128_EPS << nl4;
    }
    

    Das führt bei meinem g++ mit Quadmath Library zur folgenden Ausgabe.

    FLT_RADIX                                                                                  2    
                               float             double           long double         __float128    
                 sizeof               4                  8                 16                 16    
     binary digits mant              24                 53                 64                113    
    decimal digits mant               6                 15                 18                 33    
       minimum exponent            -125              -1021             -16381             -16381    
       maximum exponent             128               1024              16384              16384    
    minimum 10 exponent             -37               -307              -4931              -4931    
    maximum 10 exponent              38                308               4932               4932    
                minimum       1.175e-38         2.225e-308        3.362e-4932       +3.362e-4932    
                maximum       3.403e+38         1.798e+308        1.190e+4932       +1.190e+4932    
                epsilon       1.192e-07          2.220e-16          1.084e-19          1.926e-34    
    
    

    Wichtig ist in diesem Kontext, dass man versteht, dass die Gleitkommazahlen dieser Datentypen binärkodiert sind und keine Dezimalzahlen sind. Meines Wissen ist IBMs POWER Plattform die zur Zeit einzige Hardwareplattform, die in der Lage ist Dezimalgleitkommaarithmetik in Hardware zu machen.


Anmelden zum Antworten