Große Zahlen



  • Hallo zusammen,
    ich beschäftige mich gerade mit Datentypen und mit sehr großen Zahlen. Ich möchte gerne die Fakultät von 1000 berechnen. Über die Forensuche habe ich folgenden Quelltext gefunden, den ich jedoch nicht ganz verstehe. Könnt ihr mir dabei helfen?

    class Biggy 
    { 
    public: 
        Biggy( double x = 1.0 ) 
            : m_x( std::log10( x ) ) 
        {} 
        Biggy& operator*=( const Biggy& b ) 
        { 
            m_x += b.m_x; 
            return *this; 
        } 
        friend std::ostream& operator<<( std::ostream& out, const Biggy& b ) 
        { 
            const double g = std::floor( b.m_x ); 
            const std::streamsize prec = out.precision(); 
            out << std::pow( 10., b.m_x - g ) << "E" << (g>0?"+":"") << std::fixed << std::setprecision(0) << g; 
            out.precision( prec ); 
            return out; 
        } 
    
    private: 
        double m_x; 
    };
    

    Was wird hier genau gemacht? Ich habe das mal versucht aufzudröseln, doch ich verstehe nicht so genau, welche Strategie hier vorhanden ist, um dem Compiler große Zahlen vorzugaulen:

    Also: Ich deklariere die Klasse "Biggy" und erstelle einen Konstruktor mit dem Standardwert 1.0 als Parameter. Dieser ist vom Typ double!. Mittels eines Elementinitialisierers weise ich der Variable "m_x" den Logarithmus des Parameters zu.

    1.) Warum arbeitet man hier mit dem Logarithmus? Wird die zu berechnende Zahl dadurch nicht geändert?

    2.) Was ist m_x für ein Attribut? Wo ist die Deklaration?

    Vielen Dank für die Hilfe
    lg, freakC++



  • freakC++ schrieb:

    1.) Warum arbeitet man hier mit dem Logarithmus? Wird die zu berechnende Zahl dadurch nicht geändert?

    Bei der Ausgabe wird ja wieder 10^m_x berechnet. Auch die Multiplikation ist passend implementiert, also passt das schon.

    2.) Was ist m_x für ein Attribut? Wo ist die Deklaration?

    Da wo double m_x; steht?


  • Mod

    freakC++ schrieb:

    Was wird hier genau gemacht?

    Dies ist nicht wirklich eine Bibliothek für große Zahlen allgemein, sondern ein Trick wenn man nur große Zahlen multiplizieren will. Wobei mir nicht klar ist, wo hier der Vorteil liegen soll, außer dass man damit die (ohnehin schon gewaltig große) obere Schranke von double überwinden kann (wenn man Unendlich nicht mitzählt).

    Also: Ich deklariere die Klasse "Biggy" und erstelle einen Konstruktor mit dem Standardwert 1.0 als Parameter. Dieser ist vom Typ double!.

    Jupp. Siehe oben, es ist nicht wirklich eine Bibliothek für große Zahlen.

    Mittels eines Elementinitialisierers weise ich der Variable "m_x" den Logarithmus des Parameters zu.

    1.) Warum arbeitet man hier mit dem Logarithmus? Wird die zu berechnende Zahl dadurch nicht geändert?

    Die Zahl wird dadurch geändert. Aber a*b=c ➡ log(a)+log(b)=log(c)

    2.) Was ist m_x für ein Attribut? Wo ist die Deklaration?

    Zeile 22.

    edit: Ach ja: Für die Fakultät von 1000 ist das nicht geeignet, da fehlt einfach die Präzision. Zwar ist dies tatsächlich ein Fall wo man die obere Grenze von double sprengen würde (1000! ~ 4*10^2567, double_max ~ 2*10^308), aber du bekämst nur ein paar korrekte Stellen. Für genaue Ergebnisse empfehle ich dir als Suchworte "C++ BigInt"



  • Hallo,

    oh ja: Zeile 22! Da stehts ja! Ich habe wohl zu weit oben gesucht.

    Heißt das, dass die Zahl trotzdem nicht größer als double werden kann, weil m_x vom Typ double ist?

    Du sprichst von fehlender Präzision. Sind die Ergebnisse etwa fehlerhaft?

    Ich verstehe diese friend Methode nicht richtig. Könnt ihr mir da nochmal auf die Sprünge helfen?

    Es wird der << Operator überladen. Zunächst wir eine Zahl gerundet. Handelt es sich dabei um die Zahl, die ausgegeben werden soll? Was soll die Rundungsgeschichte?

    Mit

    const std::streamsize prec = out.precision();
    

    wird die Genauigkeit von Fließkommazahlen gespeichert. Was soll das hier?

    Danke für eure Hilfe!
    lg, freakC++



  • !1000

    402.387.260.077.093.773.543.702.433.923.003.985.719.374.864.210.714.632.543.799.
    910.429.938.512.398.629.020.592.044.208.486.969.404.800.479.988.610.197.196.058.
    631.666.872.994.808.558.901.323.829.669.944.590.997.424.504.087.073.759.918.823.
    627.727.188.732.519.779.505.950.995.276.120.874.975.462.497.043.601.418.278.094.
    646.496.291.056.393.887.437.886.487.337.119.181.045.825.783.647.849.977.012.476.
    632.889.835.955.735.432.513.185.323.958.463.075.557.409.114.262.417.474.349.347.
    553.428.646.576.611.667.797.396.668.820.291.207.379.143.853.719.588.249.808.126.
    867.838.374.559.731.746.136.085.379.534.524.221.586.593.201.928.090.878.297.308.
    431.392.844.403.281.231.558.611.036.976.801.357.304.216.168.747.609.675.871.348.
    312.025.478.589.320.767.169.132.448.426.236.131.412.508.780.208.000.261.683.151.
    027.341.827.977.704.784.635.868.170.164.365.024.153.691.398.281.264.810.213.092.
    761.244.896.359.928.705.114.964.975.419.909.342.221.566.832.572.080.821.333.186.
    116.811.553.615.836.546.984.046.708.975.602.900.950.537.616.475.847.728.421.889.
    679.646.244.945.160.765.353.408.198.901.385.442.487.984.959.953.319.101.723.355.
    556.602.139.450.399.736.280.750.137.837.615.307.127.761.926.849.034.352.625.200.
    015.888.535.147.331.611.702.103.968.175.921.510.907.788.019.393.178.114.194.545.
    257.223.865.541.461.062.892.187.960.223.838.971.476.088.506.276.862.967.146.674.
    697.562.911.234.082.439.208.160.153.780.889.893.964.518.263.243.671.616.762.179.
    168.909.779.911.903.754.031.274.622.289.988.005.195.444.414.282.012.187.361.745.
    992.642.956.581.746.628.302.955.570.299.024.324.153.181.617.210.465.832.036.786.
    906.117.260.158.783.520.751.516.284.225.540.265.170.483.304.226.143.974.286.933.
    061.690.897.968.482.590.125.458.327.168.226.458.066.526.769.958.652.682.272.807.
    075.781.391.858.178.889.652.208.164.348.344.825.993.266.043.367.660.176.999.612.
    831.860.788.386.150.279.465.955.131.156.552.036.093.988.180.612.138.558.600.301.
    435.694.527.224.206.344.631.797.460.594.682.573.103.790.084.024.432.438.465.657.
    245.014.402.821.885.252.470.935.190.620.929.023.136.493.273.497.565.513.958.720.
    559.654.228.749.774.011.413.346.962.715.422.845.862.377.387.538.230.483.865.688.
    976.461.927.383.814.900.140.767.310.446.640.259.899.490.222.221.765.904.339.901.
    886.018.566.526.485.061.799.702.356.193.897.017.860.040.811.889.729.918.311.021.
    171.229.845.901.641.921.068.884.387.121.855.646.124.960.798.722.908.519.296.819.
    372.388.642.614.839.657.382.291.123.125.024.186.649.353.143.970.137.428.531.926.
    649.875.337.218.940.694.281.434.118.520.158.014.123.344.828.015.051.399.694.290.
    153.483.077.644.569.099.073.152.433.278.288.269.864.602.789.864.321.139.083.506.
    217.095.002.597.389.863.554.277.196.742.822.248.757.586.765.752.344.220.207.573.
    630.569.498.825.087.968.928.162.753.848.863.396.909.959.826.280.956.121.450.994.
    871.701.244.516.461.260.379.029.309.120.889.086.942.028.510.640.182.154.399.457.
    156.805.941.872.748.998.094.254.742.173.582.401.063.677.404.595.741.785.160.829.
    230.135.358.081.840.096.996.372.524.230.560.855.903.700.624.271.243.416.909.004.
    153.690.105.933.983.835.777.939.410.970.027.753.472.000.000.000.000.000.000.000.
    000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.
    000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.
    000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.
    000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000
    

    Ein double schluckt das nicht, noch nicht einmal eine 8192 Bit-Zahl könnte das speichern.


  • Mod

    freakC++ schrieb:

    Heißt das, dass die Zahl trotzdem nicht größer als double werden kann, weil m_x vom Typ double ist?

    Doch, da ja nur der Logarithmus gespeichert wird, kann die Zahl so groß werden wie 10 hoch größter_doublewert.

    Du sprichst von fehlender Präzision. Sind die Ergebnisse etwa fehlerhaft?

    Sie sind richtig, aber nicht genau. Was manche Leute als falsch ansehen würden. Wenn du die Fakultät von 1000 berechnest, würdest du eben herausbekommen, dass diese 4.0238726007709 * 10^2567 ist, aber nicht das exakte Ergebnis

    4023872600770937735437024339230039857193748642107146325437999104299385\
    1239862902059204420848696940480047998861019719605863166687299480855890\
    1323829669944590997424504087073759918823627727188732519779505950995276\
    1208749754624970436014182780946464962910563938874378864873371191810458\
    2578364784997701247663288983595573543251318532395846307555740911426241\
    7474349347553428646576611667797396668820291207379143853719588249808126\
    8678383745597317461360853795345242215865932019280908782973084313928444\
    0328123155861103697680135730421616874760967587134831202547858932076716\
    9132448426236131412508780208000261683151027341827977704784635868170164\
    3650241536913982812648102130927612448963599287051149649754199093422215\
    6683257208082133318611681155361583654698404670897560290095053761647584\
    7728421889679646244945160765353408198901385442487984959953319101723355\
    5566021394503997362807501378376153071277619268490343526252000158885351\
    4733161170210396817592151090778801939317811419454525722386554146106289\
    2187960223838971476088506276862967146674697562911234082439208160153780\
    8898939645182632436716167621791689097799119037540312746222899880051954\
    4441428201218736174599264295658174662830295557029902432415318161721046\
    5832036786906117260158783520751516284225540265170483304226143974286933\
    0616908979684825901254583271682264580665267699586526822728070757813918\
    5817888965220816434834482599326604336766017699961283186078838615027946\
    5955131156552036093988180612138558600301435694527224206344631797460594\
    6825731037900840244324384656572450144028218852524709351906209290231364\
    9327349756551395872055965422874977401141334696271542284586237738753823\
    0483865688976461927383814900140767310446640259899490222221765904339901\
    8860185665264850617997023561938970178600408118897299183110211712298459\
    0164192106888438712185564612496079872290851929681937238864261483965738\
    2291123125024186649353143970137428531926649875337218940694281434118520\
    1580141233448280150513996942901534830776445690990731524332782882698646\
    0278986432113908350621709500259738986355427719674282224875758676575234\
    4220207573630569498825087968928162753848863396909959826280956121450994\
    8717012445164612603790293091208890869420285106401821543994571568059418\
    7274899809425474217358240106367740459574178516082923013535808184009699\
    6372524230560855903700624271243416909004153690105933983835777939410970\
    0277534720000000000000000000000000000000000000000000000000000000000000\
    0000000000000000000000000000000000000000000000000000000000000000000000\
    0000000000000000000000000000000000000000000000000000000000000000000000\
    000000000000000000000000000000000000000000000000
    

    Ich verstehe diese friend Methode nicht richtig. Könnt ihr mir da nochmal auf die Sprünge helfen?

    Was verstehst du daran nicht? Der Operator<< ist eine freie Funktion (auch wenn er hier innerhalb der Klasse definiert wird), daher muss er friend sein um auf die Interna der Klasse zugreifen zu können. Dies ist eine der üblichen Implementierungen von Operator << für Streams.

    Es wird der << Operator überladen. Zunächst wir eine Zahl gerundet. Handelt es sich dabei um die Zahl, die ausgegeben werden soll? Was soll die Rundungsgeschichte?

    10 hoch 5.678 = (10 hoch 0.678)*(10 hoch 5)

    Mit

    const std::streamsize prec = out.precision();
    

    wird die Genauigkeit von Fließkommazahlen gespeichert. Was soll das hier?

    Weil die Präzision während der Ausgabe geändert wird, ist es guter Stil sie danach wieder auf den Ursprungswert zurückzusetzen.



  • super! Danke für die Antworten.

    Ich wollte jetzt eine Methode implementieren, die zwei Biggy Zahlen dividiert. Warum geht das nicht?

    Biggy& operator /( const Biggy& b )
        {
            return m_x / b.m_x;
        }
    

    Hier fehlt angeblich ein L - Wert? Hä?

    Was mache ich falsch?

    Vielen Dank
    lg, freakC++



  • freakC++ schrieb:

    super! Danke für die Antworten.

    Ich wollte jetzt eine Methode implementieren, die zwei Biggy Zahlen dividiert. Warum geht das nicht?

    Biggy& operator /( const Biggy& b )
        {
            return m_x / b.m_x;
        }
    

    Hier fehlt angeblich ein L - Wert? Hä?

    Was mache ich falsch?

    Vielen Dank
    lg, freakC++

    / sollte keine Referenz auf einen lebenden Wert zurückgeben, sondern eine neuen Wert.



  • Ach verdammt! Der Referenzoperator! Den hab ich vergessen beim Copy & Paste!

    Danke! Jetzt funktionierts!

    lg, freakC++



  • Wenn du einen guten Compiler hast und eine hohe Warnstufe einstellst, sollte die Rückgabe von Referenzen und Zeigern auf lokale/temporäre Objekte eine Warnung ausgeben.



  • Ich habe doch noch ein Problem mit dem Zugriff auf die gespeicherten Zahlen. Ich habe jetzt folgendes:

    Biggy& operator /( const Biggy& b )
        {
           m_x = m_x / b.m_x; //In b.max steht nicht die Fakultät von 30
           return *this;
        }
    
    //...
    
     Biggy a(fac<Biggy>(50));
     Biggy b(fac<Biggy>(30));
     Biggy c(a / b);
    

    Die Division klappt aber nicht. Es kommt Murks raus. Ich denke, es liegt an "b.m_x", da anscheinend das Ergebnis von 30! nicht dort abgelegt ist.

    Ich weiß aber nicht, wie ich das Problem lösen kann? Was übersehe ich da gerde?

    Vielen Dank
    lg, freakC++


  • Mod

    freakC++ schrieb:

    Die Division klappt aber nicht. Es kommt Murks raus. Ich denke, es liegt an "b.m_x", da anscheinend das Ergebnis von 30! nicht dort abgelegt ist.

    Ja, natürlich. Hast du den Thread überhaupt gelesen? Hast du nicht verstanden, was die Idee hinter der Klasse ist? Weißt du überhaupt, was ein Logarithmus ist?

    Ich könnte dir zwar jetzt einfach die richtige Lösung hinklatschen, aber dann denkst du ja erst recht nicht, was da überhaupt passiert. Das ist aber wichtig.



  • den sinn habe ich verstanden. Ich habe wohl vergessen, den Logarithmus wieder "rückgängig" zu machen. Ich muss also die Potenz noch nehmen.

    Ich probiere jetzt noch dran rum und melde mich dann wieder.

    edit: Bei einer Sache brauche ich aber Hilfe. Und zwar kann ich den einen Divisor nicht als stream beinhalten, sondern als Zahl. Das ist aber schwachsinn:

    double s = std::pow(10,b.m_x);
    

    denn dann hätte ich mit Biggy sparen können. Ich verstehe halt nicht, wie ich die Division durchführen muss. Beide Zahlen müssen als solche ja auch gespeichert sein und der Trick, der bei der Überladung von << genutzt worden ist, ist hier für mich nicht anwendar...

    Vielen Dank
    lg, freakC++


  • Mod

    log(a/b)=log(a)-log(b)



  • Hallo freakC++
    es ist ja amüsant zuzusehen, wie Du Dich mit der Division abmühst, aber dann sehe ich in Deinem Profil, dass Du schon(!) in der 12.Klasse bist.

    Jetzt mal 'ne Frage an die anderen: ist das normal? Wird heute nicht mehr gelehrt, was ein Logarithmus ist?

    Gruß
    Werner



  • also ich war mal auf nem technischen Gymnasium, LK Mathe + Informatik ... und mir begegnete der log auch erst im Studium ... und meine Schule ist schon 13 Jahre her!



  • Interessant - ich kann mich noch an mein erstes Semester an der Uni erinnern, da fragte der Prof wer nicht wisse was ein Logarithmus ist. Darauf meldeten sich 2 oder 3 (von ca.200). Anschließend meinte der Prof dann: "Kommen sie nach der Vorlesung bitte zu mir, ich möchte mal wissen, wie Sie zu Ihrem Abiturzeugnis gekommen sind."
    normal war das damals nicht.

    Gruß
    Werner



  • Hallo zusammen!

    Ich hatte am Ende der 10. Klasse für drei Wochen etwa den Logarithmus. Seitdem habe ich den nicht mehr durchgenommen. Ich habe es jetzt mit SeppJ Rechenregel hinbekommen.

    Meine Schwierigkeit war halt, dass der Code für mich noch etwas schwierig war und ich außerdem mit dem Logarithmus nicht allzu vertraut war. Das hat sich ja jetzt geändert 🙂

    Vielen Dank euch!
    lg, freakC++


Log in to reply