Berechnung von großen (Gleitpunkt)Zahlen



  • Hallo,

    wie kann die folgende Zeile möglichst genau berechnet werden?

    2134915546196436/537917681192752981183125556779405341338492357623302109306472651601485648799844980105978394275713428

    Die Standard-Datentypen sind dafür nicht ausreichen, da die Zahlen bereits vorher gerundet werden. Wie kann die Berechnung genau erfolgen, damit lediglich das Gesamtergebnis gerundet wird?

    Schon mal vielen Dank für eure Tipps!



  • Das Ergebnis ist 0 (sogar wie gewünsch gerundet).

    Was haste als Umgebung? Langzahlbibliothek installierbar? Einfach beide vom Compiler in einen double stecken lassen und guts ists.

    Klappt 2134915546196436.0/537917681192752981183125556779405341338492357623302109306472651601485648799844980105978394275713428.0 ?

    Haste nur Ganzzahlen kannste vielleicht immer den Zähler verzehnfachen und sobald der Z=>N ihn sooft abziehen und ${sooft} ausgeben alles bis der User die Lust verliert.


  • Mod

    Da bei einer Division der relative Fehler unverändert bleibt, kannst du auch gleich mit entsprechend gerundeten Zahlen rechnen und erhältst das gleiche Ergebnis, als hättest du erst am Ende gerundet. Bei dir ist beispielsweise das genaue Ergebnis auf 8 Stellen genau 3.9688518e-84. Rechnet man gleich mit auf 9 Stellen gerundeten Zahlen, bekommt man 3.9688517879e-84, was auf 8 Stellen gerundet wieder 3.9688518e-84 ist. Das ist kein Zufall, sondern Mathematik.

    Ansonsten: Musst du eben programmieren. So wie die schriftlichen Rechnungen in der Grundschule. Gibt's aber auch schon fertig.



  • Auf die Schnelle habe ich diese Library (leider nur für Ganzzahlen) gefunden:

    https://mattmccutchen.net/bigint/

    Wird anscheinend leider nicht mehr weiter entwickelt vom Autor.

    Von meinem ersten PC (keine DOSe), Sinclair QL, weiß ich von einem Taschenrechner-Programm vom Autor des Betriebssystems (QDOS, 32-Bit, preemptives Multitasking in 68000er Assembler programmiert), der dafür "String-Arithmetik" verwendet hat (angeblich unbegrenzte Genauigkeit, bzw. nur vom Speicher abhängig). Wie das genau geht, weiß ich aber nicht.

    Für finanzmathematische Aufgaben langt es aber oft schon aus, BCD-Zahlen zu verwenden. Dafür dürfte es für C++ sicher etliche Libraries einschließlich Operator Overloading geben.


  • Mod

    johan schrieb:

    Auf die Schnelle habe ich diese Library (leider nur für Ganzzahlen) gefunden:

    Die GNU Bibliotheken sind recht weit verbreitet (und auch die ersten Hits bei Google 😕 ):

    • GMP: Beliebig genaue Ganzzahlen, rationale Zahlen und Fließkommazahlen.
    • MPFR: Ähnlich wie GMP mit mehr Features.
    • MPC: Unterstützung für komplexe Zahlen.

    Sind natürlich C-Bibliotheken, muss man also wrappen. Haben die Leute von Boost aber - wie immer - schon gemacht:
    http://www.boost.org/doc/libs/release/libs/multiprecision/

    Was man bei all diesen Fragestellungen beachten sollte ist, ob man wirklich beliebige Genauigkeit wünscht oder ob schon eine höhere, aber endliche, Genauigkeit ausreicht.

    Und wenn man das Ergebnis sowieso rundet, dann braucht man wahrscheinlich gar nichts von alledem, sondern muss bloß seinen Kopf anstrengen (sollte man sowieso immer machen), wie man die Rechnung so hin bekommt, dass die Fehler zwischendurch klein bleiben, wie ich an dem Beispiel demonstriert habe.



  • http://www.ttmath.org/ ist auch sehr fein - Header-Only läuft super mit gcc,VS2008/2010 usw



  • Hallo,

    Dankeschön für die vielen Antworten.

    Leider habe ich das Problem nicht ganz korrekt beschrieben und deshalb vielleicht auch nicht die passende Antwort erhalten. Also nochmal ein neuer Versuch. Berechnet werden soll die folgende Formel:

    2234913546116637/546812681195752981093125556779405341338292357723303109106442651602488249799843980805878294255763456x^25 -
    8230855621557725/16687398718132110018711107079449625895333629080911349765211262561111091607661254297054391304192*x^24 + 3480401368169093/127314748520905380391777855525586135065716774604121015664758778084648831235208544136462336*x^23 - 7128643581070363/7770675568902916283677847627294075626569627356208558085007249638955617140820833992704*x^22 + 4885239585279369/237142198758023568227473377297792835283496928595231875152809132048206089502588928*x^21 - 4627605019871349/14474011154664524427946373126085988481658748083205070504932198000989141204992*x^20 + 5865082811361573/1766847064778384329583297500742918515827483896875618958121606201292619776*x^19 - 7823933447521549/431359146674410236714672241392314090778194310760649159697657763987456*x^18 - 936674789841489/13164036458569648337239753460458804039861886925068638906788872192*x^17 + 4526172036750237/1606938044258990275541962092341162602522202993782792835301376*x^16 - 1713828821103303/49039857307708443467467104868809893875799651909875269632*x^15 + 6876158081797715/23945242826029513411849172299223580994042798784118784
    x^14 -
    5138493841735941/2923003274661805836407369665432566039311865085952x^13 +
    5954245828809621/713623846352979940529142984724747568191373312
    x^12 -
    2716068399330923/87112285931760246646623899502532662132736x^11 +
    3917306946471135/42535295865117307932921825928971026432
    x^10 -
    4451622502140629/20769187434139310514121985316880384x^9 +
    1975271885241521/5070602400912917605986812821504
    x^8 -
    2698492929024059/4951760157141521099596496896x^7 +
    5559600079402095/9671406556917033397649408
    x^6 -
    8400827854431529/18889465931478580854784x^5 +
    8979784754350487/36893488147419103232
    x^4 -
    3247741030714731/36028797018963968x^3 +
    380321282273631/17592186044416
    x^2 -
    7553149350887783/2199023255552*x +
    8641683254463363/8589934592

    Das Ergebnis ist der Wert zu einem Polynom. Allerdings erhalte ich dann bei
    einem X-Wert von 9959 als Ergebnis 0,03967 und bei
    einem X-Wert von 9960 als Ergebnis 0,04295
    was nicht korrekt ist, weil das Ergebnis von X=1 und Y=1,00000 bis X=10.000 und Y=0,00000 absteigen muss.

    Wenn die Aussage von SeppJ für das Beispiel auch noch stimmt, weshalb entsteht von 9959 auf 9960 dann dieser Fehler? Vermutlich ist das vorherige Runden auf 8 Stellen dann doch ein Problem?

    Nochmal Danke und viele Grüße!

    P S
    Ich verwende den Qt-Creator als Entwicklungsumgebung und den gcc als Compiler. Das Gesamtergebnis wird auf 5 Nachkommastellen gerundet benötigt.


  • Mod

    Wie hast du denn die Rechnung durchgeführt?

    Und wo bekommt das Programm eigentlich die Zahlen her? So wie es aussieht, stehen doch schon alle Zahlen im Voraus fest, wo ist also überhaupt das Problem?



  • Bei x=0 kommt da aber 1006024,3371947858249768614768982 raus.

    Das sieht irgendwie nach einer Reihenentwicklung aus.
    Aber da berechnet man die einzelnen Teile meist geschickter.



  • Die Variable in der Berechnung ist x und gültige Werte dazu liegen im Bereich von 1 - 10.0000.

    In die Formel hatte ich Werte von x=1 bis x=10.0000 eingesetzt und die Y-Werte dazu berechnet. Dabei sollte der jeweilige Y-Wert(Ergebnis mit 5 Nachkommastellen) kontinuierlich abfallen und nicht mehr ansteigen...

    Deshalb ist die Frage, wenn ich die Berechnung mit mehr als 8 Nachkommastellen durchführen kann, erhalte ich damit auch das gewünschten Ergebnis?

    Vielen Dank für die tolle Beteiligung an diesem Beitrag!



  • Bei mir funktioniert das problemlos (VC++ 2013)

    #include <iostream>
    #include <iomanip>
    #include <limits>
    #include <cmath>
    
    using namespace std;
    
    long double calc(long double x)
    {
    	return
    		(long double)2234913546116637 / (long double)546812681195752981093125556779405341338292357723303109106442651602488249799843980805878294255763456.0L * pow(x, 25) -
    		(long double)8230855621557725 / (long double)16687398718132110018711107079449625895333629080911349765211262561111091607661254297054391304192.0L * pow(x, 24) +
    		(long double)3480401368169093 / (long double)127314748520905380391777855525586135065716774604121015664758778084648831235208544136462336.0L * pow(x, 23) -
    		(long double)7128643581070363 / (long double)7770675568902916283677847627294075626569627356208558085007249638955617140820833992704.0L * pow(x, 22) +
    		(long double)4885239585279369 / (long double)237142198758023568227473377297792835283496928595231875152809132048206089502588928.0L * pow(x, 21) -
    		(long double)4627605019871349 / (long double)14474011154664524427946373126085988481658748083205070504932198000989141204992.0L * pow(x, 20) +
    		(long double)5865082811361573 / (long double)1766847064778384329583297500742918515827483896875618958121606201292619776.0L * pow(x, 19) -
    		(long double)7823933447521549 / (long double)431359146674410236714672241392314090778194310760649159697657763987456.0L * pow(x, 18) -
    		(long double)936674789841489 / (long double)13164036458569648337239753460458804039861886925068638906788872192.0L * pow(x, 17) +
    		(long double)4526172036750237 / (long double)1606938044258990275541962092341162602522202993782792835301376.0L * pow(x, 16) -
    		(long double)1713828821103303 / (long double)49039857307708443467467104868809893875799651909875269632.0L * pow(x, 15) +
    		(long double)6876158081797715 / (long double)23945242826029513411849172299223580994042798784118784.0L * pow(x, 14) -
    		(long double)5138493841735941 / (long double)2923003274661805836407369665432566039311865085952.0L * pow(x, 13) +
    		(long double)5954245828809621 / (long double)713623846352979940529142984724747568191373312.0L * pow(x, 12) -
    		(long double)2716068399330923 / (long double)87112285931760246646623899502532662132736.0L * pow(x, 11) +
    		(long double)3917306946471135 / (long double)42535295865117307932921825928971026432.0L * pow(x, 10) -
    		(long double)4451622502140629 / (long double)20769187434139310514121985316880384.0L * pow(x, 9) +
    		(long double)1975271885241521 / (long double)5070602400912917605986812821504.0L * pow(x, 8) -
    		(long double)2698492929024059 / (long double)4951760157141521099596496896.0L * pow(x, 7) +
    		(long double)5559600079402095 / (long double)9671406556917033397649408.0L * pow(x, 6) -
    		(long double)8400827854431529 / (long double)18889465931478580854784.0L * pow(x, 5) +
    		(long double)8979784754350487 / (long double)36893488147419103232.0L * pow(x, 4) -
    		(long double)3247741030714731 / (long double)36028797018963968.0L * pow(x, 3) +
    		(long double)380321282273631 / (long double)17592186044416.0L * pow(x, 2) -
    		(long double)7553149350887783 / (long double)2199023255552.0L * x +
    		(long double)8641683254463363 / (long double)8589934592;
    }
    
    int main(void)
    {
    	cout << setiosflags(ios_base::scientific);
    	cout << "Setting cout precision to " << numeric_limits< long double >::digits10 << endl;
    	cout.precision(numeric_limits< long double >::digits10);
    
    	for (int i = 0; i < 10; ++i)
    	{
    		cout << "x = " << long double(i) << " f(x) = " << calc(long double(i)) << endl;
    	}
    
    	cout << "\n\nf(9959) = " <<  calc(long double(9959)) << endl;
    	cout << "\n\nf(9960) = " <<  calc(long double(9959)) << endl;
    
    	return 0;
    }
    

    Ausgabe

    Setting cout precision to 15
    x = 0.000000000000000e+000 f(x) = 1.006024337194786e+006
    x = 1.000000000000000e+000 f(x) = 1.002611091398601e+006
    x = 2.000000000000000e+000 f(x) = 9.992405456630456e+005
    x = 3.000000000000000e+000 f(x) = 9.959121678264987e+005
    x = 4.000000000000000e+000 f(x) = 9.926254314630381e+005
    x = 5.000000000000000e+000 f(x) = 9.893798158300950e+005
    x = 6.000000000000000e+000 f(x) = 9.861748058165179e+005
    x = 7.000000000000000e+000 f(x) = 9.830098918910384e+005
    x = 8.000000000000000e+000 f(x) = 9.798845700511376e+005
    x = 9.000000000000000e+000 f(x) = 9.767983417723103e+005
    
    f(9959) = 2.338740549736074e+003
    
    f(9960) = 2.338740549736074e+003
    

    Wobei VC++20113 long double auf double reduziert.



  • Ist das hier vielleicht ein X/Y-Problem?



  • Hallo,

    so fängt der Tag gut an 😋 Vielen Dank an alle und besonders für das tolle Beispiel von trunc 👍

    Ein kleiner Fehler ist noch in dem Beispiel, die Berechnung wurde 2x mit 9959 durchgeführt. Diese Kleinigkeit korrigiert und schon funktioniert es auch bei mir 😉

    Nachfolgend noch meine Version die mit dem GCC 4.5.2 erstellt wurde:

    g++ main.cpp -o app.exe
    

    main.cpp

    #include <iostream>
    #include <iomanip>
    #include <limits>
    #include <cmath>
    
    using namespace std;
    
    long double calc(long double x)
    {
        return
            (long double)2234913546116637 / (long double)546812681195752981093125556779405341338292357723303109106442651602488249799843980805878294255763456.0L * pow(x, 25) -
            (long double)8230855621557725 / (long double)16687398718132110018711107079449625895333629080911349765211262561111091607661254297054391304192.0L * pow(x, 24) +
            (long double)3480401368169093 / (long double)127314748520905380391777855525586135065716774604121015664758778084648831235208544136462336.0L * pow(x, 23) -
            (long double)7128643581070363 / (long double)7770675568902916283677847627294075626569627356208558085007249638955617140820833992704.0L * pow(x, 22) +
            (long double)4885239585279369 / (long double)237142198758023568227473377297792835283496928595231875152809132048206089502588928.0L * pow(x, 21) -
            (long double)4627605019871349 / (long double)14474011154664524427946373126085988481658748083205070504932198000989141204992.0L * pow(x, 20) +
            (long double)5865082811361573 / (long double)1766847064778384329583297500742918515827483896875618958121606201292619776.0L * pow(x, 19) -
            (long double)7823933447521549 / (long double)431359146674410236714672241392314090778194310760649159697657763987456.0L * pow(x, 18) -
            (long double)936674789841489 / (long double)13164036458569648337239753460458804039861886925068638906788872192.0L * pow(x, 17) +
            (long double)4526172036750237 / (long double)1606938044258990275541962092341162602522202993782792835301376.0L * pow(x, 16) -
            (long double)1713828821103303 / (long double)49039857307708443467467104868809893875799651909875269632.0L * pow(x, 15) +
            (long double)6876158081797715 / (long double)23945242826029513411849172299223580994042798784118784.0L * pow(x, 14) -
            (long double)5138493841735941 / (long double)2923003274661805836407369665432566039311865085952.0L * pow(x, 13) +
            (long double)5954245828809621 / (long double)713623846352979940529142984724747568191373312.0L * pow(x, 12) -
            (long double)2716068399330923 / (long double)87112285931760246646623899502532662132736.0L * pow(x, 11) +
            (long double)3917306946471135 / (long double)42535295865117307932921825928971026432.0L * pow(x, 10) -
            (long double)4451622502140629 / (long double)20769187434139310514121985316880384.0L * pow(x, 9) +
            (long double)1975271885241521 / (long double)5070602400912917605986812821504.0L * pow(x, 8) -
            (long double)2698492929024059 / (long double)4951760157141521099596496896.0L * pow(x, 7) +
            (long double)5559600079402095 / (long double)9671406556917033397649408.0L * pow(x, 6) -
            (long double)8400827854431529 / (long double)18889465931478580854784.0L * pow(x, 5) +
            (long double)8979784754350487 / (long double)36893488147419103232.0L * pow(x, 4) -
            (long double)3247741030714731 / (long double)36028797018963968.0L * pow(x, 3) +
            (long double)380321282273631 / (long double)17592186044416.0L * pow(x, 2) -
            (long double)7553149350887783 / (long double)2199023255552.0L * x +
            (long double)8641683254463363 / (long double)8589934592;
    }
    
    int main(void)
    {
        cout << setiosflags(ios_base::scientific);
        cout << "Setting cout precision to " << numeric_limits< long double >::digits10 << endl;
        cout.precision(numeric_limits< long double >::digits10);
    
        for (int i = 0; i < 10; ++i)
        {
            cout << "x = " << (long double)i << " f(x) = " << calc(i) << endl;
        }
    
        cout << "\n\nf(9959) = " <<  calc(9959) << endl;
        cout << "f(9960) = " <<  calc(9960);
    
        return 0;
    }
    

    Ausgabe

    Setting cout precision to 18
    x = 0.000000000000000000e+000 f(x) = 1.006024337194785825e+006
    x = 1.000000000000000000e+000 f(x) = 1.002611091398601383e+006
    x = 2.000000000000000000e+000 f(x) = 9.992405456630455492e+005
    x = 3.000000000000000000e+000 f(x) = 9.959121678264987193e+005
    x = 4.000000000000000000e+000 f(x) = 9.926254314630381053e+005
    x = 5.000000000000000000e+000 f(x) = 9.893798158300950234e+005
    x = 6.000000000000000000e+000 f(x) = 9.861748058165179073e+005
    x = 7.000000000000000000e+000 f(x) = 9.830098918910383546e+005
    x = 8.000000000000000000e+000 f(x) = 9.798845700511375314e+005
    x = 9.000000000000000000e+000 f(x) = 9.767983417723102716e+005
    
    f(9959) = 2.323934689140187402e+003
    f(9960) = 2.249770189624356135e+003
    

    Wünsche allen noch eine angenehme Woche!



  • Ich hoffe, dir ist klar, was bei

    (long double)546812681195752981093125556779405341338292357723303109106442651602488249799843980805878294255763456.0L
    

    intern passiert?!

    long double d = (long double)546812681195752981093125556779405341338292357723303109106442651602488249799843980805878294255763456.0L;
    
    cout << setprecision(100) << d << endl;
    

    Die Berechnung wird also niemals die Genaugkeit haben, welche durch die 100-Ziffern-Zahl (anscheinend) ausgedrückt werden soll.



  • Th69 schrieb:

    Die Berechnung wird also niemals die Genaugkeit haben, welche durch die 100-Ziffern-Zahl (anscheinend) ausgedrückt werden soll.

    Daher der nic ("trunc").



  • mit Horner-Schema bekommt man immerhin die hohen Potenzen x^25, x^24 usw weg.


Log in to reply