Lizenz: OPL,GPL Datum: 019990223@983, 019990301@705, 019990402@102
Daten Alphanumerisch Alphabetisch Numerisch Binär Festkomma Vorzeichenlos Vorzeichen-behaftet Gleitkomma einfache Genauigkeit doppelte Genauigkeit Dezimal(BCD) gepackt(binär) ungepackt(gezont) ASCII EBCDIC
Beispiel: Darstellung der binären Zahl +110,010b (= +6,25d) in Festkommadarstellung:
0 | 0 | 1 | 1 | 0 | 0 | 1 | 0 |
Vorzeichen | Vorkommateil | Nachkommateil |
Negative Zahlen werden meistens mittels Zweier Komplement dargestellt. Der Wertebereich der Zahlen wird dabei die Anzahl der zur Verfügung stehenden Bits eingeschränkt: Die größte positive Zahl ist dann +(2^n-1), die kleinste negative Zahl -(2^n), wobei n die Anzahl der Bit ist, die für die Darstellung der Zahl (excl. Vorzeichen) zur Verfügung stehen.
Beispiel: n:= 8 Bit
Vorzeichenlos (alle 8 Bit werden für die nicht negative Zahl verwendet): Wertebereich von 0 bis
255 (unsigned char)
Vorzeichenbehaftet (erstes Bit ist für das Vorzeichen der Zahl zuständig, 0 positiv, 1 negativ):
Wertebereich (-128 bis +127)(char)
Vorteile:
(16,0 Bit Festkommadarstellung) Zahl VZ Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z +47,0 => 0 0 0 0 0 0 0 0 0 0 1 0 1 1 1 1 -47,0 => 1 1 1 1 1 1 1 1 1 1 0 1 0 0 0 1 (8,8 Bit Festkommadarstellung) Zahl VZ Z Z Z Z Z Z Z,Z Z Z Z Z Z Z Z + 5,3 => 0 0 0 0 0 1 0 1,0 1 0 0 1 1 0 0 -19,2 => 1 1 1 0 1 1 0 0,1 1 0 0 1 1 0 1
A B C + D E F ---------------- := C+F + B+E 0 + A+D 0 0 ================ = G A+D B+E C+FBei der Addition/Subtraktion kann man leicht die einzelnen Teile addieren, es muß nur der Übertrag beachtet werden. Genauigkeitsprobleme gibt es keine, einzig ein Überlauf (Symbol G) ist möglich.
A , B * C , D ------------------ B*D + A*D 0 + C*B 0 + 0 A*C 0 0 ================== = E F , G HMultiplikationen sind auch relativ leicht zu machen, indem man die einzelnen Teile multipliziert. Das Ergebnis benötigt maximal doppelt so viel Platz wie die Operanden.
A B , D : D , - -------------------------- A:D 0 , + R+B/D, + ,R+C:D + , 0 R:D + , 0 0 R:D + ... ==========================ist wie man sieht relativ leicht zu lösen. Die Division der allgemeinen Form (Division durch große Zahlen) A B D E ist leider nur mit rechenintensiveren Vergleichs und Bitoperationen beizukommen.
Zur Verdeutlichung der Festkomma Arithmetik habe ich ein Beispielprogramm in Assembler geschrieben, das die Zahl PI nähert.
Das Programm basiert auf der alternierenden Summe der Kehrwerte ungerader Zahlen:
Pi/4 = 1/1 - 1/3 + 1/5 - 1/7 + 1/9 - 1/11 + ... => 3,1415926 ... (In Derives Notation: SUM(4*(-1)"(n+I)/(n*2-1),n,1,10000) )
BCD, Binary Coded Decimal
Das BCD Format ist ein Mittelding zwischen der Speicherung in ASCII und der binären Speicherung der Zahlen.
Das normale BCD Format sieht für jede Dezimalstelle ein Byte vor (verschwendet also einen Großteil), und das gepackte BCD Format speichert in jedem Byte 2 Dezimalstellen ab, indem jeweils 4 Bits für eine Dezimalziffer verwendet werden.
Das BCD Format hat den Vorteil, daß man zum Beispiel bei kaufmännischen Anwendungen die Dezimal-Genauigkeit genau festlegen kann, und keine Rundungs/Überlauffehler hat.
Nachteile sind die Speicherverschwendung und das Jahr 2000 Problem.
Gleitkommadarstellung
Bei dieser Darstellung wird die Position des Dezimalpunktes einer Zahl zusätzlich
dynamisch geregelt.
Unangenehmerweise ist es möglich, ein und dieselbe Zahl auf verschiedenste Arten darzustellen. Zum Beispiel: 0,123 = 123 * 10^-3 = 1230 * 10^-4 = 12300 * 10^-5 Darum wurde für die Gleitkommadarstellung die normierte Form entwickelt: Die Mantisse ist eine Zahl, deren Vorkommateil 1 ist. Einzigster Problemfall: Die Zahl 0. Die restliche Information der Zahl ist im Nachkommateil der Mantisse und im Exponenten der Zahl.
Normiert man z.B. die binäre Zahl +11001,1011 sieht das Ergebnis folgendermaßen aus: +1,10011011 * 10^100 Da die Ziffer vor dem Komma also fast immer 1 ist (Ausnahme: 0), kann man auf deren Abspeicherung verzichten und gewinnt so ein zusätzliches Mantissenbit, wodurch die Genauigkeit erhöht werden kann. Diese Genauigkeit wird allerdings mit der Sonderbehandlung von 0 erkauft.
Es muß also nur das Vorzeichen (1 Bit), die sogenannte Mantisse und der Exponent abgespeichert werden.
Charakteristik
In der Praxis treten positive Exponenten häufiger auf als negative.
Man vergrößert daher den Bereich für positive Exponenten auf Kosten der
negativen Exponenten.
Das geschieht dadurch, daß zum Wert des Exponenten ein konstanter Betrag k addiert wird.
Diese Konstante k wird als Charakteristik bezeichnet.
Bsp.: k=4; darzustellender Wert: 173d
r
+173 = 10101101 binär = 0,101011010 * 2^8 normalisiert.
Darstellung des Exponenten 8-4 = 4 = 100 binär.
An diesem Beispiel kann man die Vergrößerung in Richtung der positiven Zahlen deutlich sehen: Die Zahl 173 könnte ohne Charakteristik in diesem Format gar nicht dargestellt werden, da der Exponent 8 ist (Binär: 1000) und 3 Bits im Exponenten für die Darstellung von 8 nicht reichen würden.
IEEE Formate:
Gesamtlänge | Vorzeichen | Exponent | Mantisse | Charakteristik | C/C++ Bezeichnung |
32 Bit | 1 Bit | 8 Bit | 23 Bit | 127 | float |
64 Bit | 1 Bit | 11 Bit | 52 Bit | 1023 | double |
Beispiel: Wie werden die folgenden Zahlen in der Programmiersprache C abgespeichert?
void main { float a = + 5.0; //0x40A00000 float b = + 6.0; //0x40C00000 float c = -25.5; //0xC1CC0000 float d = - 6.0; //0xC0800000 float e = 0.0; //0x00000000 } Schritt 1: a = + 5,0d Schritt 2: a = 101b * 10b^0 Schrift 3: a = 1,01b * 10b^(10 + 01111111=10000001) Schrift 4: a = \,01b * 10b^(10 + 01111111=10000001) gespeichert wird: 0 10000001 01000000000000000000000 V Exponent Mantisse Speicherstelle von a: 0x40A00000 Schritt 1: c = - 25,5d Schritt 2: c = - 11001,lb * 10b^0 Schritt 3: c = - l,10011b * 10b^(110 + 01111111 = 10000011) Schritt 4: c = - \,10011b * 10b^(110 + 01111111 = 10000011) gespeichert wird: 1 10000011 100110000000000000000000 V Exponent Mantisse Speicherstelle von c: 0xC1CC0000
Allgemeine Numerische Probleme
Ungenauigkeiten durch die binäre Darstellung
Reelle Zahlen können im Gleitkommaformat nicht immer exakt dargestellt werden.
Dadurch kann es bei der Berechnung von arithmetischen Ausdrücken zu
Ungenauigkeiten kommen.
Beispiel: 0,1d=0,00011001100110011...b Beispiel 0,11; 0,2; 0,4, usw. können nicht genau dargestellt werden.
Daher: Zahlen im Gleitkommaformat (float,double,...) nie auf Gleichheit prüfen! Statt dessen Prüfung auf > oder < oder E-Bereich.
int main() { float i=0.0; for (i=0.0; i != 1.0; i += 0.1) { /* Endlosschleife, weil 0.1 nicht exakt dargestellt werden kann. */ printf("%f",i); }; return(0); }; int main() { float i=0.0; for (i=0.0; i<0.9999 || i>1.0001; i += 0.1) { /* Durch die Prüfung auf den E-Bereich von 0,0001 ist es jetzt keine Endlosschleife mehr. */ printf("%f",i); }; return(1); };
A * B / 100 = 00000 A *(B / 100) = 90000 (A * B) / 100 = 00000Also: Auf die Reihenfolge der Auswertung achten!
es gilt: a + (b + c) <> (a + b) + c a * (b + c) <> (a * b) + (a * c) Beispiel (8 Bit Format): a = 11,0000 b = 0,0000 11 c = 0,0000 1 a = 11,0000 + (b+c)= 0,0001 -- ================= a+ (b+c)= 11,0001 -- (a+b) = 11,0000 -- +C = 0,0000 -- ==================== (a+b)+c = 11,0000 -- Wie man sieht, sind beide Ergebnisse unterschiedlich.
29 mod 2 = 1 29 div 2 = 14 14 mod 2 = 0 14 div 2 = 7 7 mod 2 = 1 7 div 2 = 3 3 mod 2 = 1 3 div 2 = 1 1 mod 2 = 1 1 div 2 = 0 => Fertig29 mod 2 gibt 1. 29 div 2 gibt 14, und 14 wird für die nächste Rechenoperation verwendet. Der Vorgang wird solange wiederholt, bis das Ergebnis der Divison 0 ist. Da das Ergebnis von unten nach oben gelesen werden muß, lautet es: 29d = 11101b.
0.815 * 2 = 1.63 0.630 * 2 = 1.26 0.260 * 2 = 0.52 0.540 * 2 = 1.04 0.040 * 2 = 0.08 ...Das Schema funktioniert folgendermaßen: Die umzuwandelnde Zahl wird mit der Basis des Zielsystems multipliziert. Die Vorkommastelle des Ergebnisses ist die erste Ziffer des Endergebnisses. Die Nachkommastelle des Ergebnisses wird für die nächste Multiplikation verwendet. Im Beispiel wird das Ergebnis nur auf 5 Nachkommastellen genau ausgerechnet. Das Ergebnis wird bei diesem Rechenverfahren von oben nach unten gereiht, und sieht folgendermaßen aus: 0,11010b.
Beispiel: 010 1010 1l10,1111 0001 1000b = 2AE,F18h
Die Umwandlung in die umgekehrte Richtung ist genau so leicht zu realisieren. Jede hexadezimale Ziffer entspricht einer Vierergruppe im Binären System. Die Tatsache, daß die Konvertierung zwischen diesen beiden Zahlendarstellungen so leicht durchgeführt werden kann und die kompakte Darstellung sind die Hauptgründe für die Verbreitung des hexadezimalen Zahlensystems innerhalb der Informatik.
Daten Alphanumerisch Alphabetisch Numerisch Binär Festkomma Vorzeichenlos Vorzeichen-behaftet Gleitkomma einfache Genauigkeit doppelte Genauigkeit Dezimal(BCD) gepackt(binär) ungepackt(gezont) ASCII EBCDIC
Beispiel: Darstellung der binären Zahl +110,010b (= +6,25d) in Festkommadarstellung:
0 | 0 | 1 | 1 | 0 | 0 | 1 | 0 |
Vorzeichen | Vorkommateil | Nachkommateil |
Negative Zahlen werden meistens mittels Zweier Komplement dargestellt. Der Wertebereich der Zahlen wird dabei die Anzahl der zur Verfügung stehenden Bits eingeschränkt: Die größte positive Zahl ist dann +(2^n-1), die kleinste negative Zahl -(2^n), wobei n die Anzahl der Bit ist, die für die Darstellung der Zahl (excl. Vorzeichen) zur Verfügung stehen.
Beispiel: n:= 8 Bit
Vorzeichenlos (alle 8 Bit werden für die nicht negative Zahl verwendet): Wertebereich von 0 bis
255 (unsigned char)
Vorzeichenbehaftet (erstes Bit ist für das Vorzeichen der Zahl zuständig, 0 positiv, 1 negativ):
Wertebereich (-128 bis +127)(char)
Vorteile:
(16,0 Bit Festkommadarstellung) Zahl VZ Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z +47,0 => 0 0 0 0 0 0 0 0 0 0 1 0 1 1 1 1 -47,0 => 1 1 1 1 1 1 1 1 1 1 0 1 0 0 0 1 (8,8 Bit Festkommadarstellung) Zahl VZ Z Z Z Z Z Z Z,Z Z Z Z Z Z Z Z + 5,3 => 0 0 0 0 0 1 0 1,0 1 0 0 1 1 0 0 -19,2 => 1 1 1 0 1 1 0 0,1 1 0 0 1 1 0 1
Unangenehmerweise ist es möglich, ein und dieselbe Zahl auf verschiedenste Arten darzustellen. Zum Beispiel: 0,123 = 123 * 10^-3 = 1230 * 10^-4 = 12300 * 10^-5 Darum wurde für die Gleitkommadarstellung die normierte Form entwickelt: Die Mantisse ist eine Zahl, deren Vorkommateil 1 ist. Einzigster Problemfall: Die Zahl 0. Die restliche Information der Zahl ist im Nachkommateil der Mantisse und im Exponenten der Zahl.
Normiert man z.B. die binäre Zahl +11001,1011 sieht das Ergebnis folgendermaßen aus: +1,10011011 * 10^100 Da die Ziffer vor dem Komma also fast immer 1 ist (Ausnahme: 0), kann man auf deren Abspeicherung verzichten und gewinnt so ein zusätzliches Mantissenbit, wodurch die Genauigkeit erhöht werden kann. Diese Genauigkeit wird allerdings mit der Sonderbehandlung von 0 erkauft.
Es muß also nur das Vorzeichen (1 Bit), die sogenannte Mantisse und der Exponent abgespeichert werden.
Charakteristik
In der Praxis treten positive Exponenten häufiger auf als negative.
Man vergrößert daher den Bereich für positive Exponenten auf Kosten der
negativen Exponenten.
Das geschieht dadurch, daß zum Wert des Exponenten ein konstanter Betrag k addiert wird.
Diese Konstante k wird als Charakteristik bezeichnet.
Bsp.: k=4; darzustellender Wert: 173d
r
+173 = 10101101 binär = 0,101011010 * 2^8 normalisiert.
Darstellung des Exponenten 8-4 = 4 = 100 binär.
An diesem Beispiel kann man die Vergrößerung in Richtung der positiven Zahlen deutlich sehen: Die Zahl 173 könnte ohne Charakteristik in diesem Format gar nicht dargestellt werden, da der Exponent 8 ist (Binär: 1000) und 3 Bits im Exponenten für die Darstellung von 8 nicht reichen würden.
IEEE Formate:
Gesamtlänge | Vorzeichen | Exponent | Mantisse | Charakteristik | C/C++ Bezeichnung |
32 Bit | 1 Bit | 8 Bit | 23 Bit | 127 | float |
64 Bit | 1 Bit | 11 Bit | 52 Bit | 1023 | double |
Beispiel: 0,1d=0,00011001100110011...b Beispiel 0,11; 0,2; 0,4, usw. können nicht genau dargestellt werden.
Daher: Zahlen im Gleitkommaformat (float,double,...) nie auf Gleichheit prüfen! Statt dessen Prüfung auf > oder < oder E-Bereich.
int main() { float i=0.0; for (i=0.0; i != 1.0; i += 0.1) { /* Endlosschleife, weil 0.1 nicht exakt dargestellt werden kann. */ printf("%f",i); }; return(0); }; int main() { float i=0.0; for (i=0.0; i<0.9999 || i>1.0001; i += 0.1) { /* Durch die Prüfung auf den E-Bereich von 0,0001 ist es jetzt keine Endlosschleife mehr. */ printf("%f",i); }; return(1); };