Realzahl, Floating-Point
In der Mathematik bezeichnet die Menge der Reellen Zahlen alle Zahlen mit beliebigen Ziffern nach dem Komma. Da in einem Computer nur eine begrenzte Anzahl Bits zur Verfügung stehen, existieren spezielle Codierungen, welche einen genau definierten Teil des Reellen Zahlenbereiches abzubilden vermögen. All diese Codierungen werden grundsätzlich als Real-Zahl bezeichnet. In Prozessoren gibt es dementsprechend spezielle Befehle, welche mit ebendiesen Codierungen umgehen können. Während zu früheren Zeiten Codierungen wie Fixpunkt oder Binary coded decimals (BCD) verwendet wurden, hat sich seit einigen Jahrzehnten die vielseitig einsetzbare Floating-Point-Codierung durchgesetzt.
Floating-Point-Berechnungen werden in der sogenannten floating point unit
(FPU) verarbeitet. Diese speichert Realzahlen mittels der Exponentialdarstellung, sprich, einer Aufspaltung der Zahl in Mantisse und Exponent. Durch die Wahl eines geeigneten Exponenten ist es möglich, das Trennzeichen (den Punkt, beziehungsweise das Komma) zwischen Ganzzahl-Teil und Bruch-Teil so zu verschieben, dass sowohl sehr grosse wie auch sehr kleine Zahlen mit derselben Codierung gespeichert werden können. Diese Verschiebung des Trennzeichens brachte der Codierung den Namen Floating-Point-Number oder auf Deutsch Fliesskomma-
Zahl. Siehe dazu auch den Kommentar ganz unten auf der Seite.
Details
Die Fliesskomma-Codierung ist durch die IEEE 754 Norm festgelegt. Diese Codierung ist motiviert durch die Exponential-Darstellung von Dezimalzahlen. Die Zahl 0.00456
kann beispielsweise in der Exponentialdarstellung folgendermassen geschrieben werden: 4.56 * 10^-3
, oder kurz 4.56e-3
. Hierbei wird der Teil 4.56
als Mantisse
und der Teil -3
als Exponent
bezeichnet. Die dazugehörige Basis 10
ist implizit gegeben und wird einfach weggelassen. Durch diese Aufspaltung von Mantisse und Exponent können sowohl sehr grosse, wie auch sehr kleine Zahlen in kompakter Schreibweise angegeben werden. Die Fliesskomma-Codierung tut dies genauso.
Die Basis der Exponentialdarstellung wird auch Radix
genannt. Während für Menschen die Radix-10-Darstellung üblich ist, rechnen die meisten FPUs mit Radix-2, basierend auf dem Binärsystem. Es sind auch Radix-10-FPUs im Umlauf, für die folgende Erklärung wird jedoch angenommen, dass eine FPU in Radix-2 rechnet.
Eine Fliesskommazahl besitzt stets 1 Vorzeichen-Bit, welches für positive Zahlen 0
und für negative Zahlen 1
ist. Die restlichen Bits werden auf die Mantisse und den Exponent aufgeteilt. In der IEEE 754 Norm werden insbesondere zwei Bit-Verteilungen als Standard definiert: Eine 32-Bit Fliesskommazahl mit 23 Bits Mantisse und 8 Bits Exponent für einfache Genauigkeit und eine 64-Bit Fliesskommazahl mit 52 Bits Mantisse und 11 Bits Exponent für doppelte Genauigkeit. Prozessorhersteller können grundsätzlich beliebige weitere Codierungen implementieren (und das tun sie auch). In der folgenden Erklärung werden die Bitgrössen für die einfache Genauigkeit verwendet (1 Bit Vorzeichen, 23 Bits Mantisse, 8 Bits Exponent):
Der Exponent wird als vorzeichenlose Ganzzahl mit einem sogenannten Bias
(Verschiebung) codiert. Im Gegensatz zum Komplement entstehen negative Exponenten somit einfach durch Subtraktion des Bias von der vorzeichenlosen Ganzzahl. Wenn 8 Bits für den Exponenten reserviert sind und der Bias auf 127
gesetzt ist, so können die verschobenen Exponenten [0
, 255
], beziehungsweise die tatsächlichen Exponenten [-127
, 128
] codiert werden. Der jeweils kleinste und grösste Exponent ist jedoch für spezielle Zahlen reserviert (siehe unten). Somit können mit diesen Angaben schlussendlich die Exponenten [-126
, 127
] dargestellt werden.
Die Mantisse wird als vorzeichenlose Ganzzahl interpretiert. Für eine Mantisse mit 23 Bits ergibt sich somit der (Integer-) Wertebereich [0
, 8388607
]. Wenn dieser Wertebereich durch die Zahl 8388608
(=2^23) dividiert wird, so ergibt sich der (reelle) Wertebereich [0, 1). Da die Zahl 8388608
nur FAST alle Zahlen von 0 bis 9999999 (die grösste 7-Stellige Dezimalzahl) enthält, kann der reelle Wertebereich [0, 1) somit nur auf 6 Dezimalstellen (Nachkommastellen) genau abgebildet werden. Die kleinste unterscheidbare Einheit der Mantisse ist definiert als 1 / 8388608 = 1.192093e-7
. Dieser Wert wird als das sogenannte Maschinen-Epsilon
bezeichnet.
Bei der Codierung der Mantisse wird zusätzlich eine sogenannte Normalisierung
vorgenommen, was folgendes bedeutet: Die Exponentialdarstellung ist nicht eindeutig. So ist beispielsweise die Zahl 4.56e-3
identisch mit der Zahl 45.6e-4
. Diese Mehrdeutigkeit ist für eine Codierung ungeeignet. Um dem Abhilfe zu schaffen, wird definiert, dass in einer FPU nur diejenige Exponentialdarstellung gültig ist, welche vor dem Trennzeichen nur eine einzige Ziffer besitzt, welche zudem nicht Null sein darf. Bei einer Radix-2-FPU bedeutet dies somit: Das höchstwertige Bit muss 1
sein. Da dieses Bit also stets 1
sein muss, kann es auch einfach weggelassen und implizit bei Berechnungen angenommen werden, indem bei der Mantisse der Integer-Wert dieses Bits (2^23 = 8388608
) hinzuaddiert wird. Gemäss obiger Rechnung kann somit bei der reell-wertigen Mantisse im Bereich von [0, 1) einfach 1 hinzuaddiert werden. Sollte es aufgrund der beschränkten Exponenten nicht möglich sein, die Mantisse einer Zahl zu normalisieren, so handelt es sich um eine denormalisierte Zahl (siehe unten).
Schlussendlich ergibt sich folgende (Pseudo-)Berechnung für eine Fliesskomma-codierte Radix-2-Zahl:
Dezimalzahl = Vorzeichen * (1 + Mantisse / 2^BitsProMantisse) * 2^(Exponent - Bias)
+-inf, denormalisierte Zahlen, +-Null, NaN
Da der Wertebereich von Fliesskommazahlen begrenzt ist, können Overflows und Underflows auftreten. Von einem Overflow wird geredet, wenn der Absolutwert einer Fliesskomma-Zahl zu gross wird, als dass sie mit dem Exponenten dargestellt werden könnte. Wenn dies passiert, wird der Exponent auf den maximalen Wert gesetzt und die Mantisse auf 0
. Zusammen mit dem Vorzeichen wird diese Zahl interpretiert als +-infinity
, also Plus/Minus Unendlich.
Wenn der Absolutwert einer Fliesskomma-Zahl zu klein wird, als dass sie mit dem Exponenten dargestellt werden könnte, wird von einem Underflow gesprochen. Hierbei wird der Exponent auf den minimalen Wert gesetzt, die Mantisse jedoch bleibt bestehen. Da diese Mantisse kein implizites 1
vor dem Trennzeichen besitzt, handelt es sich um eine denormalisierte Zahl (auf Englisch sub-normal
). Die IEEE 754 Norm legt fest, dass denormalisierte Zahlen als gleichverteilte Zahlen hin zum Wert Null zu interpretieren sind. Diese Zahlen gelten jedoch als extrem ungenau und werden von FPUs nur aufgrund der Vollständigkeit halber unterstützt, oftmals jedoch mit starken Performance-Einbussen. Aus diesem Grund können manche FPUs so eingestellt werden, dass sie bei denormalisierte Zahlen einfach die Mantisse auf 0
setzen und die Zahl somit zu 0 runden.
Die denormalisierten Zahlen sind vergleichbar mit dem, was in manchen Wissenschaftsbereichen als Singularität
bezeichnet wird: Jegliche Zahl, welche eine bestimmte Schwelle unterschreitet, kann nicht mehr exakt dargestellt werden.
Die Zahl Null muss bei der Fliesskomma-Codierung speziell behandelt werden. Da es keine normalisierte Darstellung der Zahl Null gibt, zählt sie ebenfalls zu den denormalisierten Zahlen. Da zudem jede Zahl, also auch eine denormalisierte, stets ein Vorzeichen besitzt, können die Zahlen +0
und -0
entstehen. Die Spezialbehandlung der Zahl Null besteht somit darin, dass +0
und -0
als dasselbe aufgefasst werden müssen.
Des weiteren definieren Fliesskommazahlen noch eine weitere Spezialzahl: NaN (Not a Number). Diese Zahl entsteht bei einem undefiniertem Rechenergebnis, beispielsweise, wenn die Wurzel aus einer negativen Zahl gezogen oder Null durch Null geteilt wird. Bei der NaN-Zahl wird der Exponent auf den maximal möglichen Wert gesetzt und die Mantisse auf einen Wert ungleich Null. Die genaue Spezifikation der Mantisse kann bei anderen Quellen nachgelesen werden.
Rechenfehler, numerische Genauigkeit
Für den Programmier-Alltag sind Fliesskommazahlen normalerweise unproblematisch. Sie vermögen den reellen Zahlenbereich genügend genau abzudecken. Dennoch sind sie aufgrund der limitierten Anzahl Bits nicht immer ganz exakt.
Eine genaue Ausführung, welche Probleme bei Fliesskommazahlen auftreten können, würde den Rahmen dieser Seite bei weitem sprengen. Es sei dem Leser überlassen, sich bei anderen Quellen dahingehend zu informieren, wieso 3 * 1 / 3
die Zahl 1
ergibt, 1 / 3 * 3
hingegen jedoch die Zahl .9999999
Fliesskommazahlen in C und C++
In manchen Programmiersprachen existiert ein Datentyp namens Real
. In C und C++ hingegen werden die Typen float
, double
und long double
verwendet. Sie werden als Fliesskomma-Zahl mit unterschiedlicher Genauigkeit codiert. Der Typ float
entspricht der in der IEEE 754 Norm definierten einfachen Genauigkeit und der Typ double
entspricht der doppelten Genauigkeit. Der Typ long double
ist ein sogenannt erweiterter
Typ mit ein paar Bits mehr Genauigkeit. Die genaue Definition dieses Typs ist jedoch ein Kapitel für sich, weswegen auf andere Quellen verwiesen wird.
In C und C++ wird der Typ double
als Standardtyp angesehen. Wenn beispielsweise Typangaben zu einer Fliesskommazahl fehlen (siehe auch Typ-Promotion), wird automatisch der Typ double
angenommen. Bei festen Werten im Programmcode wird ebenfalls standardmässig der Typ double
angenommen, es sei denn, die Zahl wird mit einem Suffix versehen: f
für float
und l
für long double
.
Der Typ float
kann von einer FPU normalerweise bedeutend schneller verarbeitet werden als double
, ausserdem benötigt ein float
nur 32 Bits, ein double
hingegen deren 64. Die Genauigkeit des float
-Typs reicht für viele alltägliche Anwendungen vollkommen aus. In der Wissenschaft jedoch stösst er schnell an seine Grenzen, weswegen dort vorwiegend mit dem Typ double
programmiert wird.
Die Angaben zur Codierung der Typen sind in der float
-Bibliothek festgelegt. Es ist zu beachten, dass die dort angegebene Anzahl Bits für die Mantisse das implizit hinzugefügte Bit mitzählt.
Fixpunkt-Arithmetik
Zu früheren Zeiten wurden Zahlen mit Nachkommastellen als sogenannte Fix-Point
-Zahlen codiert, wobei einfach definiert wurde, wieviele Bits für die Zahlen vor dem Trennzeichen und wieviele nach dem Trennzeichen verwendet werden. Der Teil vor dem Trennzeichen (magnitude part
oder auch integer part
) wurde sodann als Ganzzahl (beispielsweise mittels des Zweierkomplements) interpretiert und der Teil nach dem Trennzeichen als Bruch (fractional part
). Brüche werden mathematisch als rationale Zahlen bezeichnet, weswegen Bitgrössen-Angaben von Fixpunkt-Typen oftmals mit dem Zeichen Q angegeben werden.
Fixpunkt-Zahlen sind kaum standartisiert, können jedoch einfach definiert und auch einfach nachprogrammiert werden. So gibt es beispielsweise einen Fixpunkt-Typ mit 1 Vorzeichenbit, 15 Integer-Bits und 16 Fraction-Bits. Oder einen Fixpunkt-Typ mit 1 Vorzeichenbit und 31 Fraction-Bits. Theoretisch ist auch ein 8-Byte-Typ mit 1 Vorzeichen-Bit, 4 Integer-Bits und 3 Fraction-Bits denkbar. Beispielsweise für Schulnoten. Was das Vorzeichen bedeutet, sollte jedoch unbedingt vorher mit der Lehrperson abgesprochen werden.
Durch die Angabe der Bits sind der Wertebereich und damit auch die Handhabung der Arithmetik ähnlich wie bei Ganzzahlen zwar sehr exakt festgelegt, jedoch auch stark limitiert. Beispielsweise is es dadurch kaum möglich, Zahlen wie physikalische Konstanten genügend genau abzubilden, da sie oftmals entweder sehr gross oder sehr klein sind (Beispielsweise die Masse eines Atoms: 1.66 * 10^-27
). Tatsächlich verwenden manche Technologien diese Fixpunkt-Arithmetik aber auch noch heute, für den alltäglichen Programmierbedarf hat sich jedoch die Fliesskomma-Zahl durchgesetzt.
Binary coded decimals (BCD)
Da Computer normalerweise mit Radix-2 arbeiten, haben sowohl Floating-Point-Zahlen als auch Fix-Point-Zahlen das Problem, dass sie Dezimalstellen (Zehntel, Hundertstel, Tausendstel, ...) nicht exakt abbilden können. In einigen Bereichen der Programmierung ist jedoch die exakte Handhabung von Dezimalstellen erfolgsentscheidend, beispielsweise im Finanzsektor.
Dementsprechend haben einige Prozessoren die BCD-Codierung unterstützt. Bei dieser Codierung wird eine Zahl in ihre Ziffern aufgespalten und jede Dezimal-Ziffer einzeln in 8 oder 4 Bits codiert:
Die restlichen Kombinationsmöglichkeiten der 4 oder 8 Bits werden schlicht verworfen. Bei 8 Bits ist dies eine gehörige Speicherplatzverschwendung. Die 4-Bit-Variante wurde sodann auch als gepackt
, oder auf Englisch dense
bezeichnet. Mit den entstandenen binären Zahlen konnte in den Prozessoren wie mit normalen Ganzzahlen gerechnet werden, allerdings musste vor oder nach jeder einzelnen Rechnung eine BCD-Korrektur angewendet werden. Diese Befehle hatten wohlklingende Namen wie Ascii adjust after addition
.
Heutzutage sind BCDs vermutlich so gut wie ausgestorben, weswegen auf eine genauere Betrachtung der Korrekturbefehle verzichtet wird.
Bemerkung über die verwendeten Begriffe
Es ist zu beachten, dass das Wort Realzahl nicht gänzlich korrekt ist. Rein mathematisch müsste es reelle Zahl
heissen, was jedoch im Englischen korrekt als Real number
bezeichnet wird. Durch Eindeutschung hat sich daraus der Begriff Real-Zahl
gebildet und eingebürgert. Er wird ganz vermischt manchmal Deutsch und manchmal Englisch ausgesprochen.
Des weiteren gibt es heftige Diskussionen darüber, welcher der Begriffe Fliesskomma
, Gleitkomma
, Fliesspunkt
, Gleitpunkt
, usw. korrekt ist. Alles sind mehr oder weniger unbedachte Übersetzungen vom englischen floating point
. Korrekterweise müsste es heissen:
Da dies etwas zu kompliziert ist, haben sich die obigen Begriffe eingebürgert. Je nach Situation, Hierarchie, Ort oder Institution kann das eine oder andere gelten. Auf dieser Seite wird aufgrund langjähriger Gewohnheit des Autors durchgehend die Fliesskomma-Variante verwendet.
Nächstes Kapitel: Integer-Arithmetik