programmieren-3-c/a3-6/README.md

4.7 KiB

A3-6 Ergebnisse

20 Ziffern lange Integer als Strings speichern, und mit diesen rechnen


Zunächst wird das struct int20 definiert. Dies erfolgt in der int20.h Header Datei.

Um eine Mehrfachdefinition zu vermeiden, werden hier die Präprozessoranweisungen #ifndef und #define verwendet.

Des Weiteren, werden hier die Funktionen create20, add20 und print20 deklariert.

#ifndef HEADER_INT20
#define HEADER_INT20

struct int20 {
	char digits[20];
};

extern struct int20 create20(char val[]);
extern struct int20 add20(struct int20 a, struct int20 b);
extern void print20(struct int20 num);

#endif

create20

Die create20 Funktion, erstellt aus einem char * einen int20. Hierbei wird das \0 Byte aus der Zeichenkette entfernt, sodass digits ausschließlich Ziffern von 0 bis 9 enthält.

Außerdem wird die Reihenfolge der Ziffern vertauscht, und alle Ziffern, die über die Länge der ursprünglichen Zahl hinnaus gehen, werden mit '0' aufgefüllt. Dies hat den Effekt, dass der Wert jeder Ziffer durch 10^i * d ausgedrückt werden kann, wobei d die Ziffer und i der Index ist, an der sich die Ziffer im Array befindet. Die Zahl 1337 würde also im Speicher als 73310000000000000000 stehen. Der Verständlichkeit halber, kann man sich das Array einfach spiegelverkehrt vorstellen, sodass der Index von rechts nach links aufsteigend zählt.

struct int20 create20(char val[]) {
	// int20 deklarieren
	struct int20 num;

	// Länge des übergebenen Strings herausfinden. Maximal 20
	int maxIndex = 1;
	for (; maxIndex < 20 && val[maxIndex] != '\0'; maxIndex++);

	// num.digits mit den übergebenen Ziffern auffüllen, bzw mit '0'
	for (int i = 0; i < 20; i++) {
		if (i < maxIndex) {
			num.digits[i] = val[maxIndex - i - 1];
		} else {
			num.digits[i] = '0';
		}
	}

	return num;
}

print20

Die print20 Funktion ist relativ selbsterklärend. Der Inhalt des übergebenen int20 soll wieder umgekehrt werden, und führende Nullen abgeschnitten.

Hierfür wird beim Index 19 angefangen und heruntergezählt, bis eine andere Ziffer als 0 gefunden wird. Dadurch wird der Index gefunden, bei dem die Ausgabe angefangen werden soll.

Sollte die Zahl genau 0 sein, wäre dieser Index jedoch -1. Dies muss natürlich beachtet werden.

Anschließend werden die restlichen Ziffern einfach einzeln mittels printf ausgegeben.

void print20(struct int20 num) {
	int i = 19;
	for (; i >= 0 && num.digits[i] == '0'; i--);
	if (i < 0) printf("0");
	for (; i >= 0; i--) {
		printf("%c", num.digits[i]);
	}
}

Ein alternativer Implementierungsansatz könnte z.B. die Reihenfolge der Ziffern beibehalten und in dem struct int20 hinter dem char[] einen konstanten char einfügen, der lediglich das \0 Byte enthält. In diesem Fall könnte Ausgabe statt durch die Schleife auch einfach durch printf("%s", &num.digits[i]) erfolgen.

add20

Die add20 Funktion ist dank der umgedrehten Reihenfolge der Ziffern recht simpel.

Die Ziffern der beiden zu addierenden int20s werden einzeln addiert und gegebenenfalls ein Übertrag mit aufaddiert.

Bei der Berechnung der Ergebnis-Ziffer wird die folgende Formel verwendet: a.digits[i] - '0' + b.digits[i] + carry

Da die Ziffern im ASCII Zeichensatz sequentiell hintereinander liegen, kann man um z.B. von '4' auf '7' zu kommen, einfach 2 zum char addieren. Dies wird sich hier zu Nutze gemacht. Die Werte der beiden chars werden einfach zusammen addiert, und der Wert von '0' wird einmal abgezogen, da er sonst einmal zu viel addiert wird. Anschließend wird der Übertrag aus dem vorherigen Schleifendurchlauf mit übernommen.

Nun kann das Ergebnis jedoch größer als '9' sein. In diesem Fall werden nocheinmal 10 von dem char abgezogen, und der carry wird für den nächsten Schleifendurchlauf auf 1 gesetzt.

Ist nach Ende der Schleife noch ein Übertrag vorhanden, so lag ein Overflow vor. Dies wird einfach auf der Konsole ausgegeben, doch das inkorrekte Ergebnis wird zurückgegeben.

struct int20 add20(struct int20 a, struct int20 b) {
	struct int20 c;

	int carry = 0;
	for (int i = 0; i < 20; i++) {

		char cd = a.digits[i] - '0' + b.digits[i] + carry;
		carry = 0;

		if (cd > '9') {
			cd -= 10;
			carry = 1;
		}

		c.digits[i] = cd;
	}
	if (carry != 0) {
		printf("INT20 OVERFLOW\n");
	}
	return c;
}

Anmerkung:

Da ein Overflow hier mehr oder weniger Ignoriert wird, kann man mit ein wenig Trickserei auch Subtraktionen durchführen. So ist die Zahl 99999999999999999999 z.B. äquivalent zu -1:

print20(add20(create20("1338"), create20("99999999999999999999")))

liefert als Ergebnis 1337.

Dieses Verhalten ist mit Modulo-Berechnungen zu vergleichen, oder sogar p- bzw 10-adischen Zahlen