Eine lustige Herausforderung. :) :)
Ich gehe davon aus, dass Sie Ganzzahlen beliebiger Länge wollen. Ich schlage folgenden Ansatz vor:
Betrachten Sie die binäre Natur des Datentyps "int". Denken Sie daran, einfache Binäroperationen zu verwenden, um zu emulieren, was die Schaltkreise in Ihrer CPU tun, wenn sie Dinge hinzufügen. Wenn Sie ausführlicher interessiert sind, lesen Sie diesen Wikipedia-Artikel über Halb- und Volladdierer . Sie werden etwas Ähnliches tun, aber Sie können so niedrig wie das sein - aber da ich faul bin, dachte ich, ich würde einfach darauf verzichten und eine noch einfachere Lösung finden.
Bevor wir jedoch auf algorithmische Details zum Addieren, Subtrahieren und Multiplizieren eingehen, wollen wir eine Datenstruktur finden. Eine einfache Möglichkeit besteht natürlich darin, Dinge in einem std :: vector zu speichern.
template< class BaseType >
class BigInt
{
typedef typename BaseType BT;
protected: std::vector< BaseType > value_;
};
Möglicherweise möchten Sie überlegen, ob Sie den Vektor mit einer festen Größe erstellen und vorab zuordnen möchten. Der Grund dafür ist, dass Sie für verschiedene Operationen jedes Element des Vektors - O (n) - durchlaufen müssen. Vielleicht möchten Sie sofort wissen, wie komplex eine Operation sein wird, und ein festes n macht genau das.
Nun aber zu einigen Algorithmen zur Bearbeitung der Zahlen. Sie könnten es auf logischer Ebene tun, aber wir werden diese magische CPU-Leistung verwenden, um die Ergebnisse zu berechnen. Was wir jedoch von der logischen Darstellung von Half- und FullAdders übernehmen werden, ist die Art und Weise, wie mit Carry umgegangen wird. Überlegen Sie als Beispiel, wie Sie den Operator + = implementieren würden . Für jede Zahl in BigInt <> :: value_ würden Sie diese hinzufügen und prüfen, ob das Ergebnis eine Form von Übertrag erzeugt. Wir werden es nicht bitweise tun, sondern uns auf die Art unseres BaseType verlassen (sei es lang oder int oder kurz oder was auch immer): Es läuft über.
Wenn Sie zwei Zahlen hinzufügen, muss das Ergebnis sicherlich größer sein als die größere dieser Zahlen, oder? Ist dies nicht der Fall, ist das Ergebnis übergelaufen.
template< class BaseType >
BigInt< BaseType >& BigInt< BaseType >::operator += (BigInt< BaseType > const& operand)
{
BT count, carry = 0;
for (count = 0; count < std::max(value_.size(), operand.value_.size(); count++)
{
BT op0 = count < value_.size() ? value_.at(count) : 0,
op1 = count < operand.value_.size() ? operand.value_.at(count) : 0;
BT digits_result = op0 + op1 + carry;
if (digits_result-carry < std::max(op0, op1)
{
BT carry_old = carry;
carry = digits_result;
digits_result = (op0 + op1 + carry) >> sizeof(BT)*8;
}
else carry = 0;
}
return *this;
}
Die andere arithmetische Operation verläuft analog. Heck, Sie könnten sogar die stl-Funktoren std :: plus und std :: minus, std :: times und std :: divides verwenden, ..., aber achten Sie auf den Übertrag. :) Sie können Multiplikation und Division auch mithilfe Ihrer Plus- und Minusoperatoren implementieren. Dies ist jedoch sehr langsam, da dadurch die Ergebnisse berechnet werden, die Sie bereits in früheren Aufrufen von Plus und Minus in jeder Iteration berechnet haben. Es gibt viele gute Algorithmen für diese einfache Aufgabe, verwenden Sie Wikipedia oder das Web.
Und natürlich sollten Sie Standardoperatoren implementieren wie operator<<
(verschieben Sie einfach jeden Wert in value_ für n Bits nach links, beginnend mit value_.size()-1
... oh und denken operator<
Sie an den Übertrag :), - Sie können hier sogar ein wenig optimieren, indem Sie das überprüfen grobe Anzahl von Ziffern mit size()
zuerst. Und so weiter. Dann machen Sie Ihre Klasse nützlich, indem Sie mit std :: ostream befreundet sind operator<<
.
Hoffe dieser Ansatz ist hilfreich!