Zahldarstellung im Computer
Speichereinheiten¶
1 Bit= 1 b = kleinste Einheit, speichert 0 oder 1
1 Byte= 1 B = Zusammenfassung von 8 Bit
1 Kilobyte= 1 KB = 1024 Byte
nächste 2er Potenz an 1000
1 Megabyte= 1 MB = 1024 KB
1 Gigabyte= 1 GB = 1024 MB
1 Terabyte= 1 TB = 1024 GB
Speicherung von Zahlen¶
Zur Speicherung von Zahlen wird je nach Datentyp fixe Anzahl an Bytes verwendet
Konsequenz:
pro Datentyp gibt es nur endlich viele Zahlen
es gibt jeweils größte und kleinste Zahl!
Ganzzahlen¶
Mit Bits kann man Ganzzahlen darstellen
Standardmäßig betrachtet man
entweder alle ganzen Zahlen in
oder alle ganzen Zahlen in
Integer-Arithmetik in Python¶
Integer Arithmetik in Python unterscheidet sich von vielen andern Programmiersprachen, da es __keine zu großen Integer-Zahlen__gibt.
Python kann mit beliebig großen Zahlen exakt rechnen
Ausnahme: Speicherplatz im Computer reicht nicht um die Zahl zu speichern
Extrem unwahrscheinlich: 1 MB Speicher reicht für Zahlen mit ca. binären Ziffern, also ca. Dezimalziffern.
Begründung: 1 MB sind ca. 10^6 Bytes = Bits
Ein Bit speichert eine binäre Ziffer
Um eine Dezimalziffer zu speichern, benötigt man ca. 3.32 Bits ()
Viele andere Programmiersprachen (C, C++, Java, ...) haben feste Grenzen für Integer-Zahlen
z.B. 32-Bit Integer: Zahlenbereich
z.B. 64-Bit Integer: Zahlenbereich
Werden diese Grenzen überschritten, kommt es zu einem Überlauf(engl. overflow)
z.B. in C/C++/Java:
In Python gibt es keinen Überlauf, aber
Rechenoperationen mit sehr großen Zahlen sind deutlich langsamer als mit kleinen Zahlen
Achtung: Integer-Arithmetik in NumPy¶
Aus Performance-Gründen verwendet die NumPy Bibliothek für numerische Rechnungen in Python standardmäßig 64-Bit Integer.
Exakte Arithmetik ist nur innerhalb des Bereichs gewährleistet.
Bei Überschreitung dieses Bereichs kommt es zu einem Überlauf, ähnlich wie in anderen Programmiersprachen.
Modulo-Arithmetik: größte Zahl + 1 = kleinste Zahl
Python gibt meistens eine Warnung aus, wenn ein Überlauf auftritt.
Es gibt auch die Möglichkeit, in NumPy mit größeren Integer-Typen zu rechnen, z.B. np.int128, wenn die Plattform dies unterstützt. Allerdings sind diese Typen nicht so weit verbreitet und können in manchen Umgebungen nicht verfügbar sein.
Kleinere Integer-Typen wie np.int8, np.int16, und np.int32 sind ebenfalls verfügbar, aber sie haben noch kleinere Wertebereiche und sind daher anfälliger für Überläufe.
- Rechnungen in kleineren Integer-Typen sind schneller als in größeren Integer-Typen.
import numpy as np
x = np.array([1, 2, 3], dtype=np.int64)
x[0] = 2**63-1
x[0] += 1
print(x[0])-9223372036854775808
/var/folders/xh/ww_6450d6kv5hwq5w_j_kwjh0000gn/T/ipykernel_93262/104898551.py:5: RuntimeWarning: overflow encountered in scalar add
x[0] += 1
Beispiel: unsigned 4-bit Integer¶
Um zu erklären, wie es zum Überlauf kommt, betrachten wir die Darstellung von Zahlen im Binärsystem mit 4 Bits (4 Speicherplätzen für 0/1).
4 Speicherplätze für 0/1 16 verschiedene Zahlen ()
Formel: mit
0 0 0 0
0 0 0 1
0 0 1 0
0 0 1 1
1 0 0 0
1 1 1 1
Addition wie im 10er System (mit Übertrag falls Ergebnis )
im Binärsystem (Übertrag ist hochgestellt):
Beispiel: 4-bit Integer¶
4 Speicherplätze für 0/1 16 verschiedene Zahlen ()
0 0 0 0
0 1 1 1
Beobachtung bei binärer Addition:
(hochgestellt ist der Übertrag der letzten Stelle, +1 Übertrag bleibt übrig)
daher: 1 0 0 1
1 0 0 0
1 1 1 1
Daher: 0 1 1 1+0 0 0 1= 1 0 0 0 (Überlauf )
np.int32 hat nicht viel PLatz¶
Kleinere Datentypen sind zwar schneller, haben aber deutlich weniger Platz für Zahlen.
Größter Wert für
np.int32: MilliardenAchtung: Millarden
import numpy as np
x = np.array([1], dtype=np.int32)
for i in range(17):
x[0] *= (i+1)
print(f"{i+1:2d}! = {x[0]}") 1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
6! = 720
7! = 5040
8! = 40320
9! = 362880
10! = 3628800
11! = 39916800
12! = 479001600
13! = 1932053504
14! = 1278945280
15! = 2004310016
16! = 2004189184
17! = -288522240
Achtung: Überlauf-Fehler wird zwar angezeigt, aber erst bei . Das Ergebnis ist ab schon falsch.
Codierung von Zeichen¶
Strings sind Folgen von Zeichen.
Jedes Zeichen wird durch eine Zahl codiert.
Festgelegt im Unicode-Standard (UTF-8)
Den Zahlencode eines Zeichen kann man mit der Funktion
ordherausfinden.Umgekehrt kann man mit der Funktion
chraus einem Zahlencode das entsprechende Zeichen zurückgewinnen.Oft wird der Unicode in hexadezimaler Schreibweise angegeben:
0x...
for c in "abcdefghijklmnopqrstuvwxyz":
print(f"The Unicode of '{c}' is {ord(c)}")
print("Cats are great " + chr(0x1F63B)*10)The Unicode of '&' is 38
The Unicode of 'b' is 98
The Unicode of 'c' is 99
The Unicode of 'd' is 100
The Unicode of 'e' is 101
The Unicode of 'f' is 102
The Unicode of 'g' is 103
The Unicode of 'h' is 104
The Unicode of 'i' is 105
The Unicode of 'j' is 106
The Unicode of 'k' is 107
The Unicode of 'l' is 108
The Unicode of 'm' is 109
The Unicode of 'n' is 110
The Unicode of 'o' is 111
The Unicode of 'p' is 112
The Unicode of 'q' is 113
The Unicode of 'r' is 114
The Unicode of 's' is 115
The Unicode of 't' is 116
The Unicode of 'u' is 117
The Unicode of 'v' is 118
The Unicode of 'w' is 119
The Unicode of 'x' is 120
The Unicode of 'y' is 121
The Unicode of 'z' is 122
Cats are great 😻😻😻😻😻😻😻😻😻😻
Gleitkommazahlen¶
Reelle Zahlen werden im Computer in der Regel als Gleitkommazahlen (engl. floating point numbers) gespeichert. Der Name kommt daher, dass die Position des Dezimalpunkts (bzw. Binärpunkts) “gleiten” kann, abhängig von der Größe der Zahl.
Gleitkommadarstellung¶
Folgender mathematischer Satz formalisiert die Idee der Gleitkommadarstellung:
SATZ: Zu existieren
Vorzeichen
Ziffern
Exponent sodass gilt
Darstellung ist nicht eindeutig, da z.B.
geometrische Reihe:
z.B.
Bemerkungen zum Dezimalsystem¶
Satz gilt für jede Basis
Ziffern dann
Dezimalsystem ist übliches System
, , , ,
Nichteindeutigkeit im Dezimalsystem:
Bemerkungen zum Binärsystem¶
Mit sind gekürzte Brüche genau dann als endliche Summe darstellbar (nicht als unendliche Reihe), wenn der Nenner Zweierpotenz ist:
hat Nenner mit Zweierpotenz
Folgt aus Eindeutigkeit der Primfaktorzerlegung
formaler Beweis = freiwillige Übung
z.B. keine exakte Darstellung für für
Definition von Gleitkommazahlensystemen¶
Gleitkommazahlsystem
endliche Teilmenge!
Mantissenlänge
Exponentialschranken
hat Darstellung mit
Vorzeichen
Ziffern mit oder * normalisierte Gleitkommazahl () * denormalisierte Gleitkommazahl ()
Exponent mit
Darstellung von ist eindeutig (Übung!)
Ziffer muss nicht gespeichert werden
implizites erstes Bit
Arithmetik für Gleitkommazahlen¶
Ergebnis Inf, -Inf bei Überlauf (oder 1./0.)
(Inf), (-Inf)
Ergebnis NaN, falls nicht definiert (z.B. 0./0.)
not a number (NaN)
Arithmetik ist approximativ, nicht exakt
Schlechte Kondition¶
Eine Aufgabe ist __numerisch schlecht gestellt__bzw. schlecht konditioniert, falls kleine Änderungen der Daten auf große Änderungen im Ergebnis führen
z.B. hat Dreieck mit gegebenen Seitenlängen einen rechten Winkel?
z.B. liegt gegebener Punkt auf Kreisrand?
Implementierung sinnlos, weil Ergebnis zufällig!
bessere Fragestellungen:
Hat ein Dreieck mit gegebenen Seitenlängen einen Winkel ?
Liegt ein Punkt in einem -Schlauch um den Kreisrand?
Rechenfehler¶
Aufgrund von Rechenfehlern darf man Gleitkommazahlen nie auf Gleichheit überprüfen
Statt prüfen, ob Fehler klein ist
z.B. mit
In
NumPygibt es die Funktionnp.isclose(x, y, atol=..., rtol=...), die prüft, ob zwei Gleitkommazahlenxundy“nahe beieinander” liegen. Dabei sindatolundrtolabsolute bzw. relative Toleranzen.Die Funktion überprüft, ob die Bedingung
abs(x - y) <= atol + rtol * abs(y)erfüllt ist.
import numpy as np
print("0.1 + 0.2 = 0.3?", 0.1 + 0.2 == 0.3)
print(f"0.1 is actually {0.1:.30f}")
print("(116/100)*100 = 116", (116./100)*100 == 116)
print(f"(116/100)*100 is actually {(116./100)*100:.30f}")
print("0.1 + 0.2 = 0.3 up to round-off error?", np.isclose(0.1 + 0.2, 0.3))
print("(116/100)*100 = 116 up to round-off error?", np.isclose((116./100)*100, 116))
def isOnCircle(m,r,x):
dist2 = (x[0]-m[0])**2 + (x[1]-m[1])**2
if np.isclose(dist2,r**2):
print("Yes")
else:
print("No")0.1 + 0.2 = 0.3? False
0.1 is actually 0.100000000000000005551115123126
(116/100)*100 = 116 False
(116/100)*100 is actually 115.999999999999985789145284797996
0.1 + 0.2 = 0.3 up to round-off error? True
(116/100)*100 = 116 up to round-off error? True
Variablentypen np.float32, np.float64¶
np.float32ist idR. einfache Genauigkeit nach IEEE-754-Standard4 Byte = 32 Bit * 32b = 8b Exp. + 23b Mantisse + 1b Vorz.
sog. single precision
ca. 7 signifikante Dezimalstellen
np.float64ist idR. doppelte Genauigkeit nach IEEE-754-Standard8 Byte = 64 Bit * 64b = 11b Exp. + 52b Mantisse + 1b Vorz.
sog. double precision
ca. 16 signifikante Dezimalstellen
x32 = np.array(0.1, dtype=np.float32)
x64 = np.array(0.1, dtype=np.float64)
print(f"float32: 0.1 is actually {x32:.30f}")
print(f"float64: 0.1 is actually {x64:.30f}")float32: 0.1 is actually 0.100000001490116119384765625000
float64: 0.1 is actually 0.100000000000000005551115123126