Anmelden

Archiv verlassen und diese Seite im Standarddesign anzeigen : [PCN] Der Prozessor - Tutorialthread



Ineluki
24.04.2006, 03:29
Willkommen im Tutorialthread ueber den Prozessor des Projektes Carpe Noctem.

Bevor ihr weiter lest, solltet ihr sicher gehen, folgende Posts gelesen zu haben, damit ihr wisst, worum es hier genau geht.

Was ist das PCN ? (http://www.multimediaxis.de/showthread.php?t=67162)
Wie wird hier diskutiert, was sollen wir machen ? (http://www.multimediaxis.de/showthread.php?p=1268509#post1268509)
Und wo wird nun diskutiert ? (http://www.multimediaxis.de/showthread.php?t=67353)

Dieser Thread dient dazu eine Reihe von selbstgeschriebenen Tutorials zu sammeln, um Neulingen den Einstieg in dieses Projekt sowie in die LowLevel Programmierung zu erleichtern. Schliesslich koennen nur dann alle gescheit diskutieren, wenn auch alle die selben Grundlagen haben. Und genau dazu ist dieser Thread hier da. Die Fortgeschrittenen teilen ihr Wissen mit den Anfaengern, und die Anfaenger koennen Fragen stellen, die beantwortet werden. Eventuell werden einige Fragen entfernt werden, wenn sie beantwortet wurden, bzw zu eingeflochtenen FAQ Bereichen zusammengefasst.

Wie immer bei diesem Projekt bitte kein Spam, und hier bitte auch keine Diskussionen fuehren. Dazu haben wir ja die Diskussionsthreads.

*****************************************************************************************************


Ok, schalten wir mal ein, zwei Stufen niedriger, und fangen mit dem Urschleim der Computertechnik an, damit wir sicher gehen koennen, uns prinzipiell auf einer Stufe zu bewegen. So werden die Fortgeschrittenen gebremst, weil sie sich die zeit nehmen, den Anfaengern etwas zu erklaehren, und die Anfaenger werden beschleunigt und mit den Fortgeschrittenen weitergezogen.

Als Erstes muss man sich natuerlich einmal fragen, was ein Prozessor eigentlich macht. Diese einfache Frage ist schon kaum zu beantworten, wenn man nicht ein paar Dinge voraussetzt, wie z.B. Speicher, etc. Ueber genau solche Fragen hat sich ein gewisser Herr Turing bereits im letzten Jahrtausend den Kopf zerbrochen. Er postulierte eine sogenannte Turingmaschine, welche nichts weiter tat, als Zahlen von einem unendlich langen Band einzulesen bzw. auszugeben. Hier haben wir schon den ersten externen Teil, den ein Prozessor braucht, damit er ueberhaupt etwas machen kann, den Speicher.

Wie genau der Speicher (RAM) funktioniert, brauch uns ersteinmal nicht interessieren, genau so wenig, wie es ersteinmal unerheblich ist, zu wissen wie man ihn addressiert, oder was dabei genau passiert. Fuer uns ist der RAM ein Ort, wo Daten an bestimmten Stellen stehen, und wohin man Daten speichern kann. Es gibt für den Prozessor zwei grundverschiedene Arten von Speicher, zum einen die Register und zum anderen den Ram. Die Register sind Speicherbereiche im inneren des Prozessors. Sie sind daher sehr schnell, in ihrer Anzahl begrenzt und brauchen nicht addressiert zu werden. Sie zaehlen daher nicht zum RAM. Der Ram hingegen ist ein Speicherbereich ausserhalb des Prozessors und mit ihm ueber Datenleitungen verbunden. Damit der Prozessor Daten aus dem RAM verarbeiten kann, muss er sie zuerst in eines seiner Register laden. Der Prozessor kann ausschliesslich mit seinen Registern rechnen.

Jetzt wissen wir zwar, was der Speicher ist, aber haben immer noch keine Vorstellung davon, was ein Prozessor genau macht. Ein Prozessor ist dazu da, eine Folge von einzelnen Befehlen auszufuehren. Da ein Prozessor ausschliesslich Zahlen lesen, verarbeiten und schreiben kann, ist jedem Befehl eine eindeutige Nummer zugeordnet, der sogenannte Maschinencode*. Ein weiterer wichtiger Punkt, den man verstehen muss, ist, dass der Prozessor nicht zwischen Daten und Programm unterscheiden kann. Schliesslich sind alles nur Zahlen. Auch weiss der Prozessor nichts darueber, wie die Daten im RAM angeordnet sind. Der Text "HALLO" (Text besteht auch nur aus Buchstaben, indem jedem Buchstaben genau eine Zahl zugeordnet ist, siehe ASCII) koennte genau so gut 4 einzelne Zahlen zwischen 0 und 256, 4 einzelne Zahlen zwischen -128 und 127, eine Zahl zwischen 0 und 2^32-1, eine zahl zwischen -2^31 und 2^31-1, der Anfang einer Gleitkommazahl oder eine Befehlsfolge sein. Für den Prozessor spielt das keine Rolle. Ueber Sinn und Unsinn einer Zahlenfolge entscheidet einzig und allein das Programm, das er abarbeitet.

Was ist nun dieses Ominoese Programm ? Ganz genau, auch nur eine Zahlenfolge. In einem speziellen Register (das man im Normalfall nicht beschreiben kann), dem sogenannten Instruction Pointer IP, hat der Prozessor die Addresse (was das genau ist, erklaehren wir irgendwann, jetzt sei nur gesagt, dass eine Addresse eine Zahl ist, die eine Position im RAM eindeutig beschreibt) gespeichert, wo der Maschinencode steht, der als naechstes ausgeführt werden soll. Im Normalfall funktioniert ein Prozessor also nach folgendem Schema:



1. Lese Maschinencode(Zahl) an der Addresse, auf die der IP zeigt
2. Fuehre den Befehl aus, den der Maschinencode beschreibt
3. Ruecke den IP um die Anzahl an Stellen vor, die der Befehl verlangt
4. Gehe zu 1.


Die Befehle, die der Prozessor dabei versteht, sind dabei sehr spezifisch und meist ausserordentlich beschraenkt.
Ein entsprechender befehl koennte z.B. heissen: "Addiere 1 zu der Zahl im ersten Register."
Oder: "Lade Daten von der im ersten Register gespeicherten Addresse in das zweite Register."
Selbst eine so einfache Operation, wie z.B. "Speichere die Summe aus 5 und dem Inhalt des ersten Registers im zweiten Register" ist fuer den Prozessor idR nicht in einem Befehl machbar. Die Operation muss in zwei elementarere Befehle zerlegt werden:
1. Speichere 5 im zweiten Register
2. Addiere den Inhalt des ersten Registers zum zweiten Register

Ein Prozessor macht also nichts weiter, als einfachste elementare Befehle auszuführen. Die Kunst liegt nur darin, diese einfachen Befehle so aneinander zu reihen, dass etwas komplexes und nuetzliches dabei entsteht.

Nun ist es an der Zeit uns zu fragen, was fuer Befehle ein Prozessor mindestens koennen muss, damit man mit ihm komplexe Aufgaben durchfuehren kann. Ein Prozessor, der nur den einen Befehl kennt, 1 zu seinem ersten Register zu addieren, wird uns auf Dauer keine Freude machen.


PS: Es ist schwer die Grundlagen zu erklaehren, ohne dass sich viele langweilen. Trotzdem wuerde ich Vorschlagen, dass wir vorerst dieses Niveau halten, und erst langsam anziehen, damit auch alle etwas davon haben. Immerhin gibt es hier einige, die sich noch nie mit Maschinencode oder aehnlichem beschaeftigt haben. Waere schoen, wenn auch der eine oder andere weiter machen koennte. Noetige Befehle, bedingte Spruenge und Programmverzweigung, Was sind Bits Bytes, Hex- und Binaerzahlen, FLAGS, Besonderheiten der Arithmetik von Zahlen mit begrenzter Bitbreite, Wie stelle ich ein Vorzeichen dar, RISC vs CISC, usw sind sicherlich sehr gutmuetige Themen.


* Eigentlich sind die als Zahlen codierten Prozessorbefehle nicht der Maschinencode, sondern die OpCodes. Der Maschinencode ist die Gesamtheit aus OpCodes und statischen Daten. Diese Unterscheidung ist allerdings fuer diese Einfuehrung unerheblich und wird erst im Kapitel ueber den Maschinencode relevant.

Jesus_666
26.04.2006, 14:15
Übliche Notationen von Zahlen
Zahlen. Computer arbeiten nur mit Zahlen. Genauer gesagt arbeiten sie mit Nullen und Einsen und bauen daraus den Rest zusammen. Daraus ergibt sich, daß man in der Informatik auf ganz andere Weise rechnet als sonstwo - genauer gesagt arbeitet mit anderen Zahlensystemen. (Das Oktalsystem lasse ich mal aus, weil man das eh kaum braucht.)

Das Binärsystem ist das einfachste für die IT relevante Zahlensystem - es besteht aus zwei Zahlen, 0 und 1 - oder, wie ein theoretischer Informatiker schreiben würde: {0, 1} Wenn man Binärzahlen schreibt und darauf hinweisen will, daß es sich um Binärzahlen handelt, setzt man oft ein kleines b oder eine tiefgestellte 2 an das Ende der Zahl. (Bei Dezimalzahlen schreibt man ein kleines d oder eine tiefgestellte 10.)
Ansonsten verhält es sich genau wie unser normales Dezimalsystem; wenn ich N Stellen habe und die alle "voll" (also mit der höchstmöglichen Zahl belegt) sind und ich zähle einen hoch, dann werden alle Zahlen zu Nullen und die Zahl erhält eine weitere Stelle mit dem Wert 1: 111 + 1 = 1000 (7 + 1 = 8)
Jede Stelle hatten einen doppelt so hohen Wert wie die jeweils letzte Stelle. So kann man sich alle Zahlen konstruieren:
1d = 1b
2d = 10b (1*2 + 0*1)
3d = 11b (1*2 + 1*1)
4d = 100b (1*4 + 0*2 + 0*1)
8d = 1000b (1*8 + 0*4 + 0*2 + 0*1)
32d = 100000b (is' klar)
255d = 11111111b (1*128 + 1*64 + 1*32 + 1*16 + 1*8 + 1*4 + 1*2 + 1*1)
666d = 1010011010b (512 + 128 + 16 + 8 + 2)
Im Binärsystem lassen sich einige Operationen ganz einfach durchführen. Beispielsweise kann man eine Zahl mit 2 multiplizieren, indem man sie einen Schritt nach links verschiebt: 100 * 10 = 1000 (4 * 2 = 8)

Das Hexadezimalsystem ist das wohl wichtigste Zahlensystem in der IT. Es hat 16 verschiedene Zahlen: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F}
Dieses System wird oft verwendet, weil eine der wichtigsten Einheiten der IT, das Byte, sich aus acht Bits zusammensetzt und damit 256 verschiedene Werte haben kann. Mit zwei Hex-Zahlen kann man 256 Werte darstellen (8 Bit; ine Hex-Zifer kann 4 Bit darstellen). Wenn man Hex-Zahlen schreibt setzt man oft an das Ende ein kleines h oder eine tiefgestellte 16. Durch die gängige Notation in Programmiersprachen wie C hat es sich zudem etabliert, Hexzahlen mit einem "0x" am Anfang zu schreiben. Dadurch, daß man Hexzahlen fast immer verwendet, um Bytes oder Vielfache davon zu beschreiben werden sie sehr häufig zweistellig geschrieben, besonders bei der 0x-Notation.

1d = 1h = 0x01
2d = 2h = 0x02
...
9d = 9h = 0x09
10d = Ah = 0x0A
11d = Bh = 0x0B
...
15d = Fh = 0x0F
16d = 10h = 0x10
64d = 40h = 0x40
100d = 64h = 0x64
255d = FFh = 0xFF (Höchstwert eines Bytes)

65.535 = FFFFh = 0xFFFF (Zwei Bytes)
Ich gebe hier keine Umrechenregeln an, weil das im Kopf nicht so toll geht; ich persönlich setze die Zahl in das Binärsystem um; von da aus geht die Konversion nach Hex leichter. Und die erkläre ich auch nicht, weil alle vernünftigen Menschen für sowas einen Taschenrechner benutzen.


Nebenbei, das mit den Einsen und Nullen ergibt sich daraus, daß eine Leitung im Computer entweder Strom führt oder nicht (oder, um korrekter zu sein: Entweder führt sie viel oder wenig Strom, wobei "wenig" auch 0 sein kann). Das ergibt zwei Werte.
Es gab frühe Computer, die mit anderen Zahlensystemen (beispielsweise dezimal) rechneten, die waren aber zu kompliziert und hatten keine besonders tolle Leistung.



Endigkeit und das LSB
Moment mal. Der Höchstwert eines Byte ist 0xFF und der Höchstwert von zwei Byte ist 0xFFFF. Was ist mit Werten dazwischen? Ist 1024 jetzt 0x0400 oder 0x0004? Man sollte erwarten, daß es 0x0400 ist und das stimmt auch.
Also nehmen wir jetzt unseren x86-kompatiben Rechner, speichern die Zahl ab und merken, daß der Rechner uns widerspricht: Er legt den Kram im Speicher als 0x0004 ab.

¿Qué?

Wir sind gerade in die Endigkeit gelaufen. Der Begriff "Endigkeit" (engl. endianness) bezeichnet, in welcher Reihenfolge zusammengehörige Bytes (also die Bytes, aus denen sich ein größerer Wert zusammensetzt) im Speicher abgelegt werden. Das "Ende" ist das kleinste Byte. Es gibt da drei Mögichkeiten. (Ich verwende zur Illustration mal einen längeren Wert - 0xDEADBEEF, welcher sich darstellen läßt als die Bytefolge 0xDE 0xAD 0xBE 0xEF.)

Großendig (engl. big-endian): Das Byte mit dem höchsten Wert (hier das 0xDE) wird an der niedrigsten Speicheradresse abgelegt (also links) und von da aus gehen wir weiter bis zum niedrigstwertigen Byte, das an der höchsten Adresse liegt.

Adresse | 1 | 2 | 3 | 4
Byte | DE | AD | BE | EF
Dieses System ist für uns am lesbarsten (für den Rechner aber nicht unbedingt, weil er die Zahl von links nach rechts liest).

Kleinendig (engl. little-endian): Das Byte mit dem niedrigsten Wert (hier das 0xEF) wird an der niedrigsten Speicheradresse abgelegt (also links) und von da aus gehen wir weiter bis zum höchstwertigen Byte, das an der höchsten Adresse liegt.

Adresse | 1 | 2 | 3 | 4
Byte | EF | BE | AD | DE
Für uns macht das wenig Sinn, aber der Rechner liest so die niedrigstwertigen Stellen zuerst, was manchmal von Vorteil sein kann.

Mittelendig (engl. middle-endian): Wild durcheinander gewürfelt.

Adresse | 1 | 2 | 3 | 4
Byte | AD | EF | DE | BE
Byte | DE | BE | EF | AD
und so weiter...
Eklig, eklig, eklig. Dieser Unfug wird heutzutage glücklicherweise nicht mehr betrieben.

Wir haben von den Arabern übernommen, wie die Zahlen geschrieben werden; für uns sind die Zahlen großendig, für Araber hingegen kleinendig (weil bei denen ja die ganze Schrift von rechts nach links geht).
Beim Rechner können die Zahlen auch so oder so verstanden werden; beide Methoden funktionieren. Prozessoren, die großendig arbeiten wären der Motorola 68000 oder die PowerPC-Familie. Kleinendige Prozessoren wären beispielsweise alle zu IA-32 kompatiblen Prozessoren (x86, AMD64). Der Intel Itanium und einige PowerPC-Prozessoren beherrschen beide Endigkeiten (man kann umschalten).

Durch die Endigkeit ergeben sich mitunter Probleme - wenn man eine kleinendig gespeicherte Datei in einem großendigen System liest, ohne sie zu übersetzen, kriegt man Datenmüll. Umgekehrt geau so. Ist klar, es sind ja alle größeren Zahlen verdreht. Dieses Problem wird manchmal als das NUXI- oder XINU-Problem bezeichnet (weil UNIX auf vielen Plattformen läuft und so ziemlich als erstes in dieses Problem gelaufen ist).
Vermeiden kann man diesen Kram nur, indem man bei seinem Datenformat festlegt, welche Endigkeit es hat - und bei allen Implementationen auf Systemen mit der falschen Endigkeit einen Übersetzer einbaut. Man kann natürlich auch die Endigkeit offen lassen und überall Übersetzen eibauen.

Beide Endigkeiten haben ihre eigenen Vor- und Nachteile:
Bei Großendigen Zahlen kann man ganz einfach überprüfen, ob sie positiv oder negativ sind: Man liest das erste Bit, fertig. Außerdem wird dieses Format von TCP/IP und Java verwendet.
Kleinendige Zahlen kann man ganz einfach von zwei auf vier Byte verlängern: Man fügt hinten ein 0x0000 an. Großendige Zahlen müßte man zwei Byte nach hinten verlegen. Die Rückumwandlung ist uch einfach: Man verwirft die beiden letzten Bytes, fertig. An der Adresse ändert sich nichts. Außerdem verwenden IA-32-Rechner dieses Format.


Oh, da hätte ich fast das LSB vergessen. Das Least Significant Bit ist einfach das Bit mit dem niedrigsten Wert - das Bit, das die Eins darstellt. Bei kleinendigen Zahlen ist das LSB an der niedrigsten Speicheradresse, bei großendigen Zahlen an der höchsten. Das Gegenstück zum LSB ist das MSB (Most Significant Bit).

drunken monkey
28.04.2006, 23:56
So, nachdem J ja gerade (wenn auch mit Schönheitsfehler *hust*) die Speicherung natürlicher/positiver Zahlen in einem Computer erklärt hat, wäre die logische Fortsetzung die Frage, wie ganze (also auch negative) und reelle Zahlen (Kommazahlen) in einem Computer gespeichert werden. Da für die Kommazahlen die ganzen Zahlen gebraucht werden, hier erstmal

Darstellung ganzer Zahlen

Dabei gibt es unterschiedliche Möglichkeiten, jede mit eigenen Vor- und Nachteilen.

Darstellung durch Vorzeichen und Betrag:
Die wohl intuitivste Darstellung, die Zahl wird einfach mit einem zusätzlichen Bit ausgestattet, dass das Vorzeichen darstellt. Dabei bedeutet 0 "+" und 1 "-". Beispiel: -18, bei 1 Byte Wortlänge: "1 0010010"
Es gibt mehrere Nachteile dabei, z.B. dass die 0 sowohl positiv als auch negativ dargestellt werden kann, was bei Vergleichsoperationen ein getrenntes Behandeln der Null erfordert, da sonst "+0 != -0" gelten könnte. Außerdem muss bei Rechen- und Vergleichsoperationen das Vorzeichen getrennt behandelt werden.

Exzessdarstellung:
Dabei wird zu der Zahl ein fixer Wert dazuaddiert, üblicherweise etwa die Hälfte der höchsten (mit dieser Bitanzahl) darstellbaren Zahl. Im Fall von 1 Byte Wortlänge also 127. Somit sind damit Zahlen von -127 (Darstellung: 00000000) bis +128 (Darstellung: 11111111) darstellbar. Die Darstellung der 0 wäre damit 01111111 (=127).
Vorteile: Nur eine Darstellung der Null
die Darstellung ist ordnungserhaltend (kleinere Zahlen bleiben kleiner, wichtig für Vergleichsoperationen)
Nachteile: Bei arithmetischen Operationen muss der Exzess berücksichtigt werden, beispielsweise muss er vom Ergebnis einer Addition wieder abgezogen werden, da er sonst doppelt dazuaddiert würde.

Einerkomplementdarstellung:
Ein umrechnunstechnisch sehr dankbares System, da eine negative Zahl einfach aus der positiven besteht, bei der sämtliche Ziffern gespiegelt wurden. Beispiel:

+44: "00101100"
-44: "11010011"
Nachteile: Wieder doppelte Darstellung der 0; Ordnung bleibt zwar innerhalb der positiven/negativen Zahlen erhalten, jedoch sind negative Zahlen "größer" als positive; zusätzlicher Rechenaufwand bei arithmetischen Operationen durch doppelte Null-Darstellung

Zweierkomplement:
Die Zahl wird wie beim Einerkomplement gebildet, danach wird noch 1 dazugezählt. Beispiel:

+44: "00101100"
-44: "11010100"
Dadurch ergibt sich eine eindeutige Darstellung der 0 und daher auch ein geringerer Rechenaufwand bei arithmetischen Operationen.

In der Praxis werden (AFAIK) zumeist das Zweierkomplement und die Exzessdarstellung verwendet.


Kommazahlen

Etwas komplizierter ist die Darstellung von Kommazahlen. Dabei gibt es grundsätzlich zwei Ansätze, den logischeren und den besseren. ^^

Fixkommadarstellung*:
Das ist die Darstellung, die wohl auf Anhieb am logischsten erscheint. Es werden einfach für jede Zahl eine bestimmte Anzahl an Vor- und Nachkommastellen gespeichert. Eine Fixkommazahl aus 4 Byte (32 Bit) könnte demnach so aussehen:

Z VVVVVVVVVVVVVVVVV NNNNNNNNNNNNNNN
1 01010001011011110 010111010101000
Wobei Z für das Vorzeichen (0: +; 1: -), V für die Vorkomma- und N für die Nachkommastellen steht. Die dargstellte Zahl wäre in diesem Fall - 41 694,364501953125.
Der Wertebereich einer solchen Fixkommazahl (16 Vor-, 15 Nachkommastellen) aus 4 Bytes läge zwischen +/- 65535,999969482421875.
* Ich will hier gleich darauf hinweisen, dass meist von Fixpunktzahlen gesprochen wird, da im Englischen ja ein Punkt statt eines Kommas gemacht wird. Ist mir aber egal.

Diese Darstellung hat einige Vorteile, vor allem den, dass der Prozessor nicht viel umrechnen muss, um mit den Zahlen sinnvoll operieren zu können. Die Zahlen können einfach wie ganze Zahlen addiert oder subtrahiert werden. Bei Multiplikationen ebenso, nur dass dann das Ergebnis durch den jeweiligen Skalierungsfaktor (bei dem Beispiel mit 15 Nachkommastellen: 2^15) dividiert werden muss. Bei Divisionen muss anschließend mit dem jeweiligen Faktor multipliziert werden.

Jedoch hat die Darstellung den großen Nachteil, dass sie sehr unflexibel ist. Wenn man mit dem gleichen System sehr große Zahlen (> 1 Billion) und sehr kleine (ein Billionstel) speichern will, braucht man dafür vergleichsweise viel Speicherplatz, da dafür jeweils 40 Vor- und Nachkommastellen nötig wären. Das ist aber eigentlich unnötig, da man bei einer Zahl in Billionenhöhe nicht unbedingt 40 Nachkommastellen braucht, ebenso wie bei einem Billionstel kein Mensch 40 Stellen zur Darstellung der 0 vor dem Komma will. Daher findet - trotz ebenso vorhandener Nachteile - fast ausschließlich das andere System noch Verwendung:

Gleitkommadarstellung:
Bei dieser Darstellung wird die Zahl als Produkt von Mantisse und Skalierung dargestellt. Zum Beispiel kann man die Zahl 1000 auch als 10^3 und die Zahl 3000 demnach als 3 * 10^3 darstellen. Im Computerbereich wird dabei allerdings meist 2 als Basis gewählt, aufgrund der leichteren Realisierung einer Division binärer Zahlen durch diesen Faktor. Demnach wäre z.B. 24 gleich 1,5 * 2^4, oder in binärer Schreibweise: 11000 = 1,1 * 2^4. Wie man sehen kann (wenn man sich nur anstrengt ;)) wurde einfach das Komma um vier Stellen nach links verschoben und der Exponent von 2 um 4 erhöht. Analog wäre (gleich binär geschrieben) 0,001001 = 1,001 * 2^(-3). Dadurch ergibt sich ein deutlich platzsparenderes Format für Kommazahlen. Wenn man wieder von einem 4 Byte langen Wert ausgeht, ergäbe sich beispielsweise folgende Darstellung:

Z EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM
1 10001110 10100010110111100101110
Z: Vorzeichen
0 steht wieder für eine positive, 1 für eine negative Zahl.
E: Exponent
Der Exponent wird als ganze Zahl in Exzessdarstellung abgespeichert. In diesem Beispiel beträgt der Exzess 127, was einen Wert von 15 für den Exponenten bedeutet.
M: Mantisse
Diese wird als Fixpunktzahl gespeichert, wobei die erste und einzige Vorkommastelle -laut Konvention - immer 1 betragen muss. Ist das nicht der Fall, spricht man von einer "denormierten" Gleitkommazahl. Da bei einer normierten Gleitpunktzahl die erste Stelle der Mantisse immer 1 sein muss, wird auf ihre explizite Angabe zugunsten einer größeren Genauigkeit normalerweise verzichtet. Daher wäre die korrekte Darstellung eigentlich:

Z EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM
1 10001110 01000101101111001011101

Daraus ergibt sich, dass die dargestellte Zahl wieder - 41 694,36... ist, jedoch diesmal aufgrund der anderen Darstellung mit geringfügig weniger Nachkommastellen.
Man wandelt also eine Fixkommazahl in eine Gleitkommazahl um, indem man das Komma soweit verschiebt, dass die einzige Vorkommastelle eine 1 ist. Der Exponent wird dann entsprechend der Anzahl der verschobenen Stellen in Exzessdarstellung angegeben.

Daraus ergeben sich einerseits ein deutlich größerer Zahlenbereich, nämlich von +/- 2^128 maximal und +/- 2^(-127) minimal, aber auch einige Probleme:
1) Die Null ist nicht darstellbar. Das ist recht blöd, da ein Zahlensystem ohne 0 natrülich wenig Sinn macht. Daher ist die Konvention, die Null durch eine Mantisse mit lauter Nullen und einen Exponenten eins unter dem minimalen darzustellen. Dadurch ist der minimale Exponent bei einem Exzess von 127 "00000001", der Wert "00000000" ist ein Spezialwert, der bedeutet, dass die erste Stelle der Mantisse 0 ist.
2) Im Bereich um 0 bleibt aufgrund der Normierungsbedingung eine große undarstellbare Lücke. Das lässt sich ebenfalls mit dem Spezialwert lösen, da ja beim Exponenten -127 die erste Stelle der Mantisse als 0 angenommen wird. Dadurch vergrößert sich die Präzision der Gleitkommazahlen in der Nähe der Null weiter.

Für eine konkrete, genau definierte Implementierung sind weiters noch die Rundungsvorschriften wichtig, da ja längst nicht alle reellen Zahlen darstellbar sind. Daher mal im Folgenden die IEEE Norm 754, die Gleitpunktzahlensysteme normiert:

Erstmal definiert die Norm zwei Systeme, eins mit einfacher und eins mit doppelter Genauigkeit (Single/Double Precision).
Single: Bits: 32; Mantissenstellen: 23, plus implizites erstes Bit (also das erste Bit der Mantisse wird nicht geschrieben, sondern immer asl 1 angenommen); Exponentenstellen: 8; Minimaler/Maximaler Exponent: -126/+127; Exzess des Exponenten: 127.
Double: Bits: 64; Mantissenstellen: 52, plus implizites erstes Bit; Exponentenstellen: 11; Minimaler/Maximaler Exponent: -1022/+1023; Exzess des Expnenten: 1023.
Zusätzlich werden auch erweiterte Formate einfacher und doppelter Genauigkeit festgelegt, mit größeren Freiheiten in der Implementierung.

Weiters werden folgende Sonderzustände definiert:
Denormalisiert: Ist der Exponent eins kleiner als der minimale Exponent und die Mantisse ungleich 0, so handelt es sich um eine denormalisierte Gleitkommazahl, das implizite erste Bit hat also den Wert 0. Der Exponent ist allerdings trotzdem der minimale Exponent, und nicht der eins kleinere.
Null: Ist der Exponent eins kleiner als der minimale Exponent und die Mantisse ist 0, dann ist der Wert der dargestellten Zahl gleich 0. Die Darstellung besteht übrigens aus lauter Nullen, was sie auch recht intuitiv macht.
Allerdings gibt es auch hier, wie bei manchen Ganzzahlendarstellungen Werte für +0 und -0. Daher legt die IEEE Norm fest, dass dafür die Eigenschaft "-0 = +0" gilt. Jedoch wird das Vorzeichen trotzdem nicht komplett ignoriert, wodurch z.B. bei Divisionen durch Null je nach Vorzeichen +/- unendlich herauskommt.
Unendlich: Es wird auch ein Wert für den Fall bestimmt, dass die Zahl größer/kleiner ist, als die größte darstellbare, also Werte, die quasi +/- unendlich darstellen. Dazu wird der höchste Exponent genommen, eins höher als der maximal erlaubte. Die Mantisse besteht wie bei der Null aus lauter Nullen, das Vorzeichenbit unterscheidet zwischen +/- unendlich.
Nan (Not a Number): Dieser Wert wird verwendet, um das Ergebnis einer ungültigen Operation darzustellen, wie beispielsweise 0/0 oder die Wurzel aus einer negativen Zahl. Codiert wird NaN mit dem gleichen Exponenten wie Unendlich, aber mit einer Mantisse ungleich 0. Außerdem wird festgelegt, dass alle arithmetrischen Operationen mit NaN wieder NaN zurückliefern, so dass die Information über eine falsche Operation auch bei längeren Rechenvorgängen immer erhalten bleibt.

Rundung: Laut IEEE-Norm sollen alle Prozessoren als Standard die "Round to nearest"-Methode verwenden, in Verbindung mit "Round to even".
"Round to nearest" heißt, wie der Name schon sagt, dass auf den nächstliegenden darstellbaren Wert gerundet wird. "Round to even" wird eingesetzt, wenn zwei darstellbare Zahlen genau gleich weit weg von der eigentlichen Zahl liegen. Dann wird auf diejenige der beiden Zahlen gerundet, deren letzte Mantissenstelle 0 ist (= die gerade ist).
Weiters gibt es noch die Verfahren "Truncate" (alle Stellen, die auf die letzte darstellbare folgen, werden abgeschnitten) und gerichtetes Runden (Auf-/Abrunden, dürfte klar sein), die laut IEEE als aktivierbare Optionen vorhanden sein müssen.



OK, soweit zur Darstellung von Gleitkommazahlen, ich hoffe mal, ich habe nichts Wichtiges vergessen/falsch geschrieben. o_O Die Gleitkommaarithmetik hebe ich mir oder wem anderen mal als besonderes Schmankerl für später auf. -_-' Wenn irgendwas unklar sein sollte: fragt, oder noch besser, googelt. "IEEE Floating Point" liefert hunderte guter Ergebnisse.

@ Mods: Falls ihr Fehler findet, bitte bessert sie gleich aus. ^^'' Ich hab jetzt negative und Komma-Zahlen in einem gepostet, kann man ja noch immer ändern, wenn's getrennt sein sollte.