Programmieren mit C++

Code-Beispiele & Lösungen

Klassen für allgemeine Aufgaben

Klassenbibliothek für BCD-Arithmetik, rationale und beliebig große Zahlen

Die meisten Programmiersprachen sind in der Darstellung großer Zahlen beschränkt. Interpreter und Compiler vereinfachen sich die Arbeit und reservieren für eine Variable einen festen Speicherbereich. Bei den meisten Integer-Zahlen werden 16-Bit-Speicherbereiche reserviert, so daß maximal eine Darstellung von -32.768 bis 32.767 möglich ist. Long-integer-Zahlen, wie sie in vielen Programmiersprachen zur Verfügung stehen, dehnen den Bereich immerhin auf das Intervall von -2.147.483.648 bis 2.147.483.647 aus. Dies mag für die meisten Aufgaben ausreichend sein, doch bei einigen Aufgaben ist ein uneingeschränkter Zahlenbereich unverzichtbar. So finden beim Kodieren von Daten mit einem öffentlichen Schlüssel wie zum Beispiel mit Hilfe des RSA-Algorithmus große Primzahlen Verwendung, die sich mit Standarddatentypen nicht mehr darstellen lassen.

Zahlenbereiche

Ein neuer Zahlentyp soll hier Abhilfe leisten. Besonderer Wert bei den Anforderungen an diesen neuen Zahlentyp soll v. a. auf Performance, Portierbarkeit und Benutzerfreundlichkeit gelegt werden. Aus Gründen der Performance und der Speicherplatzoptimierung wird ein spezielles Speicherverfahren benutzt, das der bcd-Zahlen (binary coded decimal). Zur Darstellung einer bcd-Zahl wird jedes Byte (8 Bit) in zwei 4-Bit-Bereiche (high und low nibble) unterteilt. 4 Bits ermöglichen die Kodierung von 16 verschiedenen Zuständen, die zur Speicherung der Ziffern 0 bis 9 vollkommen ausreichen. So haben zwei Ziffern in einem Byte Platz.

Die arithmetischen Operationen werden in ähnlicher Weise durchgeführt, wie man dies von Hand tun würde. Eine Ausnahme besteht dort, wo die maschineninterne Darstellung einer Zahl eine Korrektur in die dezimale Darstellung der bcd-Zahl nötig macht. Dies geschieht beispielsweise bei der Addition, wo bei hexadezimaler Darstellung Überträge erst ab 16 vorgenommen werden. Bei der dezimalen Darstellung muß aber ein Übertrag schon bei 10 berücksichtigt werden.

Binär kodiert

Für diese Korrektur werden die vom Prozessor speziell hierfür zur Verfügung gestellten Assembler-Routinen genutzt. Bei der Addition ist dies der Assemblerbefehl daa, der die Korrektur nach einem „add with carry“ (adc) vornimmt. Nicht zuletzt deshalb und zum Zweck besserer Performance sind die eigentlichen arithmetischen Funktionen als inline-Assembler in einem eigenen Modul (bcdlow.cpp) implementiert. Die Auskapselung dieser Routinen ist zwingend notwendig, da im Gegensatz zum restlichen Source diese Routinen hardwareabhängig sind. Eine eventuelle Portierung des Programms auf eine andere Hardwareplattform macht demnach nur die Anpassung dieses Moduls notwendig.

Assembler-Routinen

Die objektorientierte Implementierung von C++ ermöglicht es, die mathematischen Operatoren für den neuen Zahlentyp zu überladen. Dies versetzt den Benutzer der neuen Klasse in die Lage, mit BCD-Zahlen in gewohnter Weise zu rechnen, und steigert die Benutzerfreundlichkeit somit enorm.

Überladen

Wie bei allen überladenen C++-Funktionen bestimmt der Compiler anhand der Anzahl und der Typen der Argumente, welche Funktion er aufrufen soll. Auch der bcd-Konstruktor ist überladen und ermöglicht die Erzeugung von Objekten, die von Initialisierungswerten abhängig sind. Der Benutzer hat somit verschiedene Möglichkeiten, eine bcd-Zahl zu initialisieren. Neben dem Standardkonstruktor der Klasse BCD, an dem keine Argumente übergeben werden, und dem Kopierkonstruktor zum Kopieren eines bcd-Klassenobjektes wird eine Initialisierung mit einer long-integer-Zahl oder einer Zeichenkette unterstützt.

Datenstruktur des bcd-Typs

Alle Daten, die für eine bcd-Zahl benötigt werden, sind zu einer Struktur zusammengefaßt worden.

bcd-Zahlen initialisieren

Sie sind gegen einen Zugriff von außen geschützt, d. h. sie können nur von Elementfunktionen und als Friends der von der Klasse BCD abgeleiteten Objekte benutzt werden. So werden unberechtigte Manipulationen an ihnen verhindert. Im einzelnen sind dies die Variablen:

  • nbytes – hier wird die Größe des zur Verfügung gestellten Speicherbereichs für eine bcd-Zahl festgelegt (Default ist 48);
  • *data referenziert den Speicherbereich, in der die eigentliche Zahl gespeichert wird;
  • sign beinhaltet das Vorzeichen der Zahl;
  • status wird als Flag für eventuell auftretende Fehler benutzt

Private Variablen

Sie stehen hierarchisch unter den öffentlichen Funktionen, da sie von ihnen benutzt werden, um Fallunterscheidungen zur weiteren Verzweigung und ähnliche Hilfsfunktionen durchzuführen. Auch private Funktionen sind gegen einen Zugriff von außen geschützt, da deren expliziete Benutzung nicht vonnöten ist. Aufzuzählen sind hier folgende Funktionen:

  • signed_add, die von den Operatoren + und - und den Funktionen lmul und ldiv benutzt wird;
  • lmul übernimmt die Durchführung der *-Operationen;
  • ldiv übernimmt die Durchführung der /-Operationen;

Alle o. g. Funktionen sind vom Typ Integer und übergeben ihren Rückgabewert dem Status flag zur Fehlerbehandlung.

  • l2bcd dient zur Umwandlung von longi-nteger-Zahlen in ein bcd-Objekt;
  • c2bcd übernimmt die Umwandlung eines Character String in eine bcd-Zahl;

Die beiden letztgenannten Funktionen dienen vor allem den Konstruktoren zur Initialisierung von bcd Variablen.

Private Funktionen

Hier sind alle dem Benutzer der bcd-Klasse zur Verfügung gestellten Funktionen zusammengeführt.

Die Konstruktoren ermöglichen unterschiedliche Arten der Initialisierung einer bcd-Variablen. Zu den Initialisierungsmöglichkeiten kommen wir später.

Alle zur Verfügung gestellten arithmetischen Funktionen sind in Form von überladenen Operatoren (+, -, *, /, %, ^, …) implementiert. Ihre Benutzung wird später erläutert Zusätzlich werden dem Benutzer noch einige Hilfsfunktionen wie test, state, cmp und decode angeboten.

Öffentliche Funktionen

Zuletzt gibt es eine Funktion, die nicht Mitglied der Klasse bcd ist, den überladenen Ausgabeoperator <<, der unter Beteiligung der Memberfunktion decode die gewohnte Ausgabe ermöglicht.

Non-Member- Funktion

Die eigentliche Durchführung einer arithmetischen Funktion überlassen alle Module den low-level-Funktionen des Moduls bcdlow.cpp. Der Zusammenhang der Module wird im nächsten Kapitel anhand eines Beispiels erläutert. Alle im Modul bcdlow.cpp zusammengefaßten Algorithmen wurden aus Performancegründen in Assembler implementiert.

Modulabhängigkeiten

Die Strukturierung aller Algorithmen folgt einer grundsätzlichen Konzeption, die am Beispiel des überladenen bcd-Operators +(bcd&) erläutert werden soll. Betrachtet wird im weiteren die Operation „a+b“, wobei davon ausgegangen wird daß es sich bei a und b jeweils um eine bcd-Zahl handelt und das Ergebnis einer weiteren bcd-Variablen, z. B. c, zugewiesen wird. An der Operation a + b sind im wesentlichen drei einzelne Module beteiligt:

die öffentliche Funktion: bcd operator + (bcd &);

die private Funktion: bcd.signed_add;

die low-level Funktion: bcd_add bzw. (bcd_sub);

Zusammenspiel

Die Aufgabe des Moduls bcd operator +(bcd&):

Eine Kopie des die Botschaft + erhaltenden Objekts a wird erzeugt. Dies geschieht, um den alten Wert der Variablen a bei der Durchführung der Funktion bcd_add (bcd_sub) nicht mit dem Ergebnis aus der Operation a + b zu überschreiben.

Ein Vergleich der Absolutbeträge der beiden Variablen a und b wird mit Hilfe der low-level Funktion memrcmp durchgeführt, um sicherzustellen, daß die Funktion signed_add mit der absolut kleineren Zahl als Argument aufgerufen wird.

Aufruf der Memberfunktion signed_add.

Den Rückgabewert der Funktion signed_add dem status flag zur weiteren Fehlerbehandlung zuweisen.

Low-Level- Funktionen

Die Aufgabe des Moduls signed_add:

Die weitere Verzweigung in die low-level-Funktionen bcd_add bzw. bcd_sub durch einen Vergleich der Vorzeichen der beiden Variablen a und b:

Vorzeichen gleich ( Durchführung von bcd_add

Vorzeichen ungleich ( Durchführung von bcd_sub

Den Rückgabewert der Assemblerroutinen bcd_add(bcd_sub) zur Fehlerbehandlung an den Operator + durchzuschleusen.

Modul signed_add

Die Aufgabe des Moduls bcd_add(bcd_sub):

Die Durchführung der eigentlichen Addition (Subtraktion)

Die Rückgabe eines Fehlercodes

Alle überladenen Operatoren der Klasse bcd sind in ähnlicher Weise durch eine Hierarchie von drei Modulen, strukturiert.

Funktionsreferenz

Modul bcd_add

bcd::bcd
1. bcd (int=48, long=0L); //Standardkonstrukor
bcd a; //48 Ziffern bcd, initialisiert //mit 0
bcd b(40); //40 Ziffern bcd, initialisiert //mit 0
bcd c(40,123456); //40 Ziffern bcd, initialisiert //mit 123456
2. bcd (int , char*);
bcd d(48,"11223344556677"); //48 Ziffern bcd,initialisiert
//durch String "11223344556677"
3. bcd (bcd &); //Kopierkonstruktor
bcd e = d; //bcd Kopie von d

Konstruktoren

Es sind drei unterschiedliche Konstruktoren definiert. Der erste kann mit keinem, einem oder zwei Argumenten gebraucht werden. Das erste Argument (Default = 40) spezifiziert die Anzahl der möglichen Ziffern einer bcd-Zahl. Das zweite Argument, eine long-integer-Zahl (Default = 0L), stellt den Initialisierungswert des neuen bcd-Objekts dar. Der zweite Konstruktor gebraucht zwei Argumente, das erste repräsentiert die Anzahl der möglichen Ziffern, das zweite in Form eines Strings den Initialisierungswert. Der dritte Konstruktor ist ein copy-Konstruktor, er übernimmt seine Initialisierungswerte von einem bereits existierenden bcd-Objekt. Alle Konstruktoren haben den impliziten Rückgabewert this, den Pointer auf das soeben kreierte Objekt.

Eine Einschränkung sei an dieser Stelle erwähnt: Operationen zwischen unterschiedlich langen bcd-Variablen werden nicht unterstützt.

Erläuterung

bcd::operator =
Gbrauch1.bcd& operator = (bcd&);
bcd a,b;
a = b; //bcd-Zuweisung zu einer anderen bcd
2.bcd& operator = (long);
c = 1234566789; //long-integer-Zuweisung zu //einer bcd
3.bcd& operator = (char*);
d = "11223344556677" //Char-String-Zuweisung //zu einer bcd

bcd::operator =

Drei Zuweisungsoperationen sind definiert, so daß die oben genannten Beispiele unterstützt werden. Alle =-Funktionen geben an das aufrufende Programm die Referenz des Objekts zurück, das die Botschaft = erhielt.

Erläuterung

bcd::bcd operator +
bcd::bcd operator -
1.bcd operator + (bcd&);
bcd operator - (bcd&);
bcd a,b,c;
c = a+b;
c = a-b;
2.bcd operator + (long);
bcd operator - (long);
a = b+123456789;
a = b-2;

bcd::operator + bcd::operator -

Die Operatoren + bzw. - sind definiert zwischen zwei bcd-Zahlen und einer bcd- und einer long integer. Die beiden Summanden bzw. Minuend und Subtrahend werden durch die Operation nicht verändert. Rückgabewert ist immer eine bcd-Zahl, in der das Ergebnis (Summe bzw. Differenz) gespeichert ist.

Erläuterung

bcd::operator ++
bcd::operator --
1.bcd& operator ++(void);
bcd& operator --(void);
bcd a,b;
a++;
b = a++;

bcd::operator ++ bcd::operator --

Die beiden Operatoren ++ bzw. -- sind so definiert, daß sie eine bcd-Zahl in gewohnter Weise inkrementieren bzw. dekrementieren, wie dies auch mit Integer-Zahlen geschieht. Rückgabewert ist jeweils die Referenz auf das Objekt, das die Botschaft ++ bzw. -- erhielt.

Erläuterung

bcd::operator *
1.bcd operator *(bcd&);
bcd a,b;
b = a * b;
2.bcd operator *(Long);
b = a * 12345;

bcd::operator *

Der Operator * ist definiert zwischen zwei bcd-Zahlen und einer bcd und einer long integer. Beide Faktoren, die an der Multiplikation beteiligt sind, werden nicht geändert. Rückgabewert ist jeweils eine bcd-Zahl, in der das Produkt gespeichert ist.

Erläuterung

bcd::operator /
1.bcd operator / (bcd);
bcd a,b;
b = a/b;
2.bcd operator / (Long);
b = a / 12345;

bcd::operator /

Der Operator / ist definiert zwischen zwei bcd-Zahlen und einer bcd und einer long integer. Sowohl der Dividend als auch der Divisor werden durch die Ausführung der Divisionsoperation nicht geändert. Rückgabewert ist jeweils eine bcd-Zahl, in der der Quotient gespeichert ist.

Erläuterung

bcd::operator %
1.bcd operator % (bcd);
bcd a,b,c;
c = a % b;
2.bcd operator % (Long);
b = a / 12345;

bcd::operator %

Der Modulo-Operator % ist definiert zwischen zwei bcd-Zahlen und einer bcd und einer long integer. Rückgabewert ist beide Male eine bcd-Zahl.

Erläuterung

bcd::operator ^ (Long)
1.bcd operator ^(Long);
bcd a, c;long b
c = a^b;
a = a^3;

bcd::operator ^ (Long)

Der überladene Operator ^ versetzt den Benutzer der bcd- Bibliothek in die Lage, eine bcd-Zahl zu exponieren. Als Exponent wird eine long-integer-Zahl unterstützt. Rückgabewert ist jeweils die Referenz auf das Objekt, das die Botschaft ^ erhielt.

Erläuterung

bcd::cmp
1.int cmp (bcd&);
bcd a,b;
a.cmp(b);
2.int cmp (Long);
bcd a;long b;
a.cmp(b);

bcd::cmp

Die Funktion cmp vergleicht ihr Objekt (hier a) mit einer anderen bcd- bzw. long-integer-Zahl. Der Rückgabewert ist negativ, wenn a < b, gleich 0, wenn a = b, und größer Null, wenn a > b ist.

Erläuterung

bcd::test
1.int test (void);
bcd a;
a.test();

bcd::test

Die Funktion test vergleicht ihr Objekt (hier a) mit Null. Der Rückgabewert ist ungleich 0, wenn a ungleich 0 ist, und 0, wenn a = 0 ist.

Erläuterung

bcd::state
1.int state(void);
bcd a;
a.state();

bcd::state

Die Funktion state liest das Status-Flag ihres Objekts aus. Mit dieser Funktion kann nachvollzogen werden, ob eventuell ein Überlauf oder eine Division mit 0 vorliegt. Der Rückgabewert ist vom Typ Integer und hat den Wert 0, wenn alles OK ist, andernfalls ist er ungleich Null.

Erläuterung

bcd::decode
1.void decode (char* bf);
bcd a;
char bf[25];
a.decode(bf);

bcd::decode

Die Funktion decode legt eine formatierte Ausgabe der in a.data enthaltenden Zahlen in den Ausgabepuffer bf. Diese Funktion wird nur zur Ausgabe durch den Operator << gebraucht und hat keinen Rückgabewert.

Sourcecode

Jeglicher Sourcecode wurde unter Borland C++ Version 3.0 implementiert und getestet. Die einzelnen Module sind :

Erläuterung

Alle wichtigen Informationen, die für die Benutzung des Zahlentyps bcd von Wichtigkeit sind, wurden für alle sichtbar im Header-File BCD.HPP abgelegt. Hier sind der Aufbau des Typs bcd und die auf ihn definierten Methoden einsehbar.

BCD.HPP

Alle in BCD.HPP definierten Methoden sind in BCD.CPP implementiert. Sie liegen hier als Source vor.

BCD.CPP

Die einzelnen arithmetischen Funktionen in BCD.CPP sind aufgrund einer besseren Performance in Assembler implementiert.

BCDLOW.CPP

Dies ist ein kleines Beispielprogramm, das die Benutzung der bcd-Klasse in einfacher Weise vorführt.

BCDTEST.CPP





Sachgebiet


© 2009-2012 by Alojado Publishing. Alle Rechte vorbehalten. Ausgewiesene Marken gehören ihren jeweiligen Eigentümern.
Mit der Benutzung dieser Seite erkennen Sie die Nutzungsbedingungen und die Datenschutzerklärung an. Der Betreiber übernimmt keine Haftung für den Inhalt verlinkter externer Internetseiten.
Seite erzeugt 2012-05-20 02:28:33 von textarchiv.alojado.de