eigener dateentyp



  • Hallo,

    ich hab ein Programm, das mit großen oder risiegem ZAhlen rechnen soll.
    Jetzt ist aber das Problem, wie kann ich einen eigenen datentyp erstellen, der genügend speicher enthält um größere daten als long zu speichern.

    gruß gucky



  • Erfinde das Rad nicht neu...

    http://gmplib.org/


  • Mod

    Wenn du trotzdem den Meißel rausholen willst um ein viereckiges Rad aus Granit zu hämmern:

    class BigNum
    {
     private:
     vector<unsigned long> ziffern;
     bool sign;
    };
    

    Dies als Grundgerüst. Und dann die üblichen Rechenoperatoren implementieren, zuerst einmal so wie in der Grundschule, später schaust du dann in ein Algorithmenbuch nach effizienten Sachen (wobei die Grundschulmethoden schon nicht so schlecht sind). Dann noch Konvertierungsoperatoren für die gängigen Integerwerte schreiben, damit es komfortabel zu benutzen ist. Und einen Ausgabe- und Eingabeoperator. Und wenn es edel sein soll noch Überladungen für die Funktionen aus der cmath. Und dann hast du schon etwas ziemlich gutes.

    edit: Semikolon nach class vergessen. Peinlich. Ich komme mir schon vor wie ein gewisser Buchautor 😉



  • ok hier hab ich mal was probiert:

    void addieren(vector<unsigned long> v1, vector<unsigned long> v2)
    {
      loop = 0;
    
      while (loop <= v1.size() || loop <= v2.size())
      {
       ziffer[loop] = v1[loop] + v2[loop];
      }
    }
    

    Dazu noch zwei fragen: 1. könnte ich die vectoren auch als referenzen übergeben?
    und 2. wie kann ich einen vector zurückgeben lassen?


  • Mod

    Gucky schrieb:

    Dazu noch zwei fragen: 1. könnte ich die vectoren auch als referenzen übergeben?

    Klar, warum nicht? Ist sogar eine gute Idee. Aber korrekterweise natürlich als const-Referenz, weil du die vectoren nicht veränderst

    und 2. wie kann ich einen vector zurückgeben lassen?

    Wie jeden anderen Datentyp auch.

    Wieso machst du eigentlich so eine Frickelei mit freien Funktionen die auf vectoren operieren? Das Problem schreit geradezu nach einem Objektorientierten Ansatz.



  • also so?

    vector addieren(const vector<unsigned long>& v1, const vector<unsigned long>& v2)
    {
      loop = 0;
    
      while (loop <= v1.size() || loop <= v2.size())
      {
       ziffer[loop] = v1[loop] + v2[loop];
      }
     return vector ziffern;
    }
    

    und was meinst du mit:

    Wieso machst du eigentlich so eine Frickelei mit freien Funktionen die auf vectoren operieren? Das Problem schreit geradezu nach einem Objektorientierten Ansatz.


  • Mod

    Gucky schrieb:

    also so?

    vector addieren(vector<unsigned long> v1, vector<unsigned long> v2)
    {
      loop = 0;
      
      while (loop <= v1.size() || loop <= v2.size())
      {
       ziffer[loop] = v1[loop] + v2[loop];
      }
     return vector ziffern;
    }
    

    Der Datentyp heißt vector<unsigned long> . Ein vector ist bloß eine Schablone (engl. Template) nach der mehr solche Datentypen gebaut werden können. Und dein Funktionskörper ist einfach nur wirr. Nochmal: vector<unsigned long> ist ein ganz normaler Datentyp wie jeder andere auch. Stell dir vor, du hättest stattdessen int als Datentyp. Würdest du dann das Programm so schreiben? Wohl kaum.

    und was meinst du mit:

    Wieso machst du eigentlich so eine Frickelei mit freien Funktionen die auf vectoren operieren? Das Problem schreit geradezu nach einem Objektorientierten Ansatz.

    Das was da steht. Was verstehst du nicht? Du schreibst doch selber in deiner Überschrift, dass du einen eigenen Datentyp möchtest. Stattdessen fängst du jetzt aber mit einen prozeduralen Ansatz an.



  • Gucky schrieb:

    also so?

    vector addieren(const vector<unsigned long>& v1, const vector<unsigned long>& v2)
    

    Wenn dann schon vector<unsigned long> und nicht einfach nur vector. Das mit den Funktionen, die Dinge zurückgeben, musst Du wohl nochmal im C++ Buch Deiner Wahl nachholen. Bei dem Tempo wird das auch erstmal nichts mit einer eigenen "big integer library". Nimm doch einfach GMP.

    Gucky schrieb:

    und was meinst du mit:

    Wieso machst du eigentlich so eine Frickelei mit freien Funktionen die auf vectoren operieren? Das Problem schreit geradezu nach einem Objektorientierten Ansatz.

    ...dass Du einen eigenen Typen definieren sollst statt vector<int> direkt zu benutzen.



  • Hey,

    lies den Artikel durch: http://c-plusplus.net/forum/232010

    Thingol 🙂



  • so hier noch mal was, aber es klappt nicht

    #include <iostream>
    #include <vector>
    using namespace std;
    
    class BigNum
    {
     BigNum () { vector<unsigned long> ziffern;}
     BigNum operator+ (const vector<unsigned long>& v1, const vector<unsigned long>& v2) const
     {
       ziffern = v1;
       return ziffern += v2;
     }
    
     private:
     vector<unsigned long> ziffern;
    };
    

    Die Velermeldungen lauten:

    "grosse_zahlen.cpp": E2080 'BigNum::operator +(const vector<unsigned long,allocator<unsigned long> > &,const vector<unsigned long,allocator<unsigned long> > &) const' muß mit einem Parameter deklariert werden in Zeile 9
    "grosse_zahlen.cpp": E2285 Keine Übereinstimmung für 'vector<unsigned long,allocator<unsigned long> >::operator =(const vector<unsigned long,allocator<unsigned long> >)' gefunden in Funktion BigNum::operator +(const vector<unsigned long,allocator<unsigned long> > &,const vector<unsigned long,allocator<unsigned long> > &) const in Zeile 10
    "grosse_zahlen.cpp": E2093 'operator+=' ist im Typ 'vector<unsigned long,allocator<unsigned long> >' für Argumente desselben Typs nicht implementiert in Funktion BigNum::operator +(const vector<unsigned long,allocator<unsigned long> > &,const vector<unsigned long,allocator<unsigned long> > &) const in Zeile 11
    


  • Gucky schrieb:

    Velermeldungen

    😮

    Schau dir den Artikel über Operatorüberladung an. Dort wird erklärt, wie die Operatoren üblicherweise überladen werden.

    Übrigens: Der Rumpf deines Konstruktors enthält eine sinnlose Deklaration.



  • Okay, du willst es wirklich selber probieren, das ist ja auch in Ordnung.
    Aber wie wäre es, wenn du statt des vector<unsinged long> vielleicht erst einmal ein vector<char> nimmst. Jeder char stellt eine Ziffer dar. Es ist natürlich schon Verschwendung, da pro Ziffer normalerweise nur 4 Bit benötigt werden, aber dies ist definitiv der einfachste Weg, seine eigene BigInt-Bibiothek zu schreiben. Später, wenn die Grundprinzipien verstanden wurden, kann es entweder umgeschrieben oder auf vorgefertigte Lösungen umgestiegen werden.
    PS: Velermeldungen 😕


  • Mod

    Vicious Falcon schrieb:

    da pro Ziffer normalerweise nur 4 Bit benötigt werden,

    😮 Willst du ihm etwa ein Zehnersystem empfehlen? unsigned long ist schon fein. Eventuell auch unsigned int, weil dies der native Datentyp des System sein 'sollte'.



  • Nein*, empfehlen würde ich eine Klasse Digit, die vieles intern regelt.
    Und einen vector<Digit> / eine deque<Digit>.
    Und ja, das Dezimalsystem ist für Lernzwecke deshalb geeignet, da man das Rechnen in diesem System bereits in der Grundschule gelernt hat. Ich habe das so mal vor einiger Zeit gemacht, es läuft wunderbar, mittlerweile mit 32 Bit-Werten, die jeweils 8 Ziffern verwalten.

    Edit: hier müsste ein Ja stehen :p


  • Mod

    Und ja, das Dezimalsystem ist für Lernzwecke deshalb geeignet, da man das Rechnen in diesem System bereits in der Grundschule gelernt hat.

    😕 Aber so ist das doch komplizierter als das native Format des Computers. Beim Dezimalsystem muss man Überläufe abfragen, bei meinem Vorschlag kann man die Überlaufeigenschaften der unsigned Arithmetik benutzen.

    edit: Oder mir ist einfach nicht bewusst, wie schwer sich andere Leute tun, in anderen Zahlensystemen zu denken. 🙂



  • Ich habe es (auch) deshalb gemacht, da man wirklich große Zahlen mit einem string initialisieren (können) muss. Wie geschrieben, es ist schon eine Zeit her.

    Number n1("15615616156156187784567894864564984894564984984894988749849489489");
    Number n2("789789456156494894894894778978974894984747894841512648945612318461231894198156");
    Number n3(n1*n2);
    n3*=10232432;
    cout << n3.AsString()<<endl;
    

    führt zur Ausgabe

    126.197.085.158.388.492.014.666.574.562.203.133.663.511.929.310.329.751.922.123.
    787.558.339.601.204.496.112.549.724.891.672.688.735.968.565.347.887.471.606.416.
    213.047.981.388.928.182.870.568.634.688
    

    Es gibt viele Wege, aber selbst auf meinem Uralt-Notebook geht dies (Compiler sei natürlich Dank) ohne spürbare Wartezeit.

    Edit: Wer so etwas macht, hat (wie ich) die Notwendigkeit von so großen Zahlen völlig überschätzt 😃



  • Vicious Falcon schrieb:

    Wer so etwas macht, hat (wie ich) die Notwendigkeit von so großen Zahlen völlig überschätzt 😃

    Größenwahn quasi 😃



  • HighLigerBiMBam schrieb:

    Größenwahn quasi 😃

    ROFL 😃



  • so nochmal

    class BigNum 
    {
    	const BigNum operator+(BigNum const& lhs, BigNum const& rhs)
    	{
    		BigNum tmp(lhs); //Kopie des linken Operanden 
    		tmp += rhs; //Implementierung mittels des +=-Operators 
    		return tmp; 
    	}
    };
    

    wobei aber mein compiler sagt, dass dem operator zu viele parameter übergeben werden 😞



  • ist auch logisch, weil du innerhalb der Klasse einem Operator immer nur rhs (du hast rhs und THIS!) übergeben musst. Wenn du zwei parameter brauchst musst du die Überladung friend machen, oder besser außerhalb der Klasse deklarieren.



  • aber wieso schreiben die das dann so hier?

    Übliche Implementierung:
    C/C++ Code: 
    const X operator+(X const& lhs, X const& rhs) 
    { 
      /* Erzeugen eines neuen Objektes, dessen Attribute gezielt einzeln gesetzt werden. Oder: */ 
      X tmp(lhs); //Kopie des linken Operanden 
      tmp += rhs; //Implementierung mittels des +=-Operators 
      return tmp; 
    }
    

Anmelden zum Antworten