Einführung in die Programmierung für Studierende der Physik

(Introduction to Programming for Physicists)

Vorlesung gehalten an der J.W.Goethe-Universität in Frankfurt am Main

(Sommersemester 2022)

von Dr.phil.nat. Dr.rer.pol. Matthias Hanauske

Frankfurt am Main 01.04.2022

Computerarithmetik mit Python

In diesem Jupyter Notebook werden wir die im Unterpunkt Die Computerarithmetik und der Fehler in numerischen Berechnungen besprochene C++ Computerarithmetik an einem Beispiel mittels der Programmiersprache Python verdeutlichen.

In der Mathematik werden Zahlen mit einer unendlichen Anzahl von Nachkommastellen zugelassen und die definierte Arithmetik auf diesem Zahlenraum ℝ ist exakt. Der Zahlenraum in dem der Computer rechnet (ℝ$_C$) benutzt jedoch Zahlen die nur eine endliche Anzahl von Nachkommastellen haben und es wird hierbei nur eine relativ kleine Teilmenge der reellen Zahlen benutzt. Diese Teilmenge ℝ$_C$ umfaßt nur rationale Zahlen und speichert den gebrochenen Teil, der mit Mantisse bezeichnet wird, zusammen mit dem exponentiellen Teil, welchen man Charakteristik nennt. Hierbei speichert der Computer die Zahl als eine binäre Liste von Nullen und Einsen.

Im Folgenden wird das Beispiel der Speicherung einer Gleitkommazahl einfacher Genauigkeit besprochen, wie sie in IBM-Großrechnern der 3000er Serie benutzt wurde (Details findet man in R.L. Burden und J.D. Faires 2000: Numerische Methoden, Kapitel 1.3 ). In diesen Rechnern besteht eine Zahl mit einfacher Genauigkeit aus einem Vorzeichen-bit, einem 7-bit Exponenten der Basis 16 und einer 24-bit-Mantisse.

Wir betrachten z.B. die binäre Computerzahl

$$ \begin{equation} \underbrace{0}_{\hbox{Vorzeichen}} \, \underbrace{1000010}_{\hbox{Charakteristik}} \, \underbrace{101100110000010000000000}_{\hbox{Mantisse}} \quad. \end{equation} $$

Das Vorzeichen-bit ist 0, was bedeutet, das die Zahl positiv ist. Die darauf folgenden 7-bit Zahlen beschreiben die Charakteristik, und diese berechnet sich wie folgt:

$$ \begin{equation} 1 \cdot 2^6 + 0 \cdot 2^5 + 0 \cdot 2^4 + 0 \cdot 2^3 + 0 \cdot 2^2 + 1 \cdot 2^1 + 0 \cdot 2^0 = 66 \end{equation} $$

Um auch kleine Zahlenwerte abbilden zu können, wird von diesem Wert 64 abgezogen, sodass man als Exponenten der Zahl $16^{66-64}=16^{2}$ erhält.

Der Zahlenwert der Mantisse ergibt sich wie folgt aus der am Ende stehenden 24-bit Zahlenfolge (die Null-Einträge sind hierbei nicht augelistet):

$$ \begin{equation} 1 \cdot \left( \frac{1}{2} \right)^1 + 1 \cdot \left( \frac{1}{2} \right)^3 + 1 \cdot \left( \frac{1}{2} \right)^4 + 1 \cdot \left( \frac{1}{2} \right)^7 + 1 \cdot \left( \frac{1}{2} \right)^8 + 1 \cdot \left( \frac{1}{2} \right)^{14} \end{equation} $$

Die eigentliche Gleitkommazahl ergibt sich nun mittels der folgenden Formel Zahl:=$\, M \cdot 16^{\,C - 64}$, wobei $M$ den Zahlenwert der Mantisse und $C$ den Wert der Charakteristik bezeichnet.

Wir definieren zunächst eine Liste der binären Computerzahl und separieren dann das Vorzeichen der Zahl von den Teillisten der Charakteristik und Mantisse.

In [1]:
Zahlenliste_Binaer = [0,1,0,0,0,0,1,0,1,0,1,1,0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0]
#Zahlenliste_Binaer = [0,1,0,0,0,0,1,0,1,0,1,1,0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1]   #Nächst größere Zahl
#Zahlenliste_Binaer = [0,1,0,0,0,0,1,0,1,0,1,1,0,0,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1]   #Nächst kleinere Zahl
#Zahlenliste_Binaer = [0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]   #Kleinste Zahl
#Zahlenliste_Binaer = [0, 1,0,0,0,0,0,1, 0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] #Die Zahl Eins
#Zahlenliste_Binaer = [0, 1,0,0,0,0,0,0, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] #Die nächst kleinere Zahl vor Eins
#Zahlenliste_Binaer = [0, 1,0,0,0,0,0,1, 0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1] #Die nächst größere Zahl nach Eins

Vorzeichen_Binaer = Zahlenliste_Binaer[0]
Charakteristik_Binaer = Zahlenliste_Binaer[1:8]
Mantisse_Binaer = Zahlenliste_Binaer[8:]

Den Wert der Charakteristik wird mittels einer for-Schleife berechnet. Hier wird die Form einer Bereichsbasierten for-Anweisungsbedingungen benutzt, die über alle Einträge der binären Zahlenliste der Charakteristik läuft. Im Vergleich zu den C++ for-Schleifen sind hier einige Unterschiede zu beachten und das Pendant einer C++ for-Schleife würde wohl folgende Struktur haben 'for (auto bin_wert : Charakteristik_Binaer){ ... }', diese Art von for-Schleifen werden wir jedoch erst später in der Vorlesung behandeln.

In [2]:
Charakteristik = 0
k=6
for bin_wert in Charakteristik_Binaer:
    Charakteristik = Charakteristik + bin_wert*2**k
    k = k - 1
Charakteristik
Out[2]:
66

In gleicher Weise berechnen wir uns den Wert der Mantisse:

In [3]:
Mantisse = 0
k=1
for bin_wert in Mantisse_Binaer:
    Mantisse = Mantisse + bin_wert*(1/2)**k
    k = k + 1
Mantisse
Out[3]:
0.69927978515625

Die entsprechende Gleitkommazahl unserer gesamten binären Zahlenliste (das Vorzeichen wird hierbei ignoriert) berechnen wir dann und speichern den Wert unter der Variable 'Zahl', die wir dann ausgeben lassen:

In [4]:
Zahl = Mantisse * 16**(Charakteristik - 64)
Zahl
Out[4]:
179.015625

Ähnlich zum C++ 'printf(...)'-Befehl geben wir nochmals die Zahl in formatierter Ausgabe auf 25 Nachkommastellen genau aus:

In [5]:
print("Die Zahl beträgt: %.25f" %Zahl )
Die Zahl beträgt: 179.0156250000000000000000000