Das Floating-Point-Format im Detail
Teil 3 von 3 / 15. März 2004 / von aths / Seite 10 von 13
Der 8087 - Wegbereiter für Floating-Point auf dem PC
Bislang haben wir etwas einseitig den Grafik-Bereich betrachtet. Heute wollen wir das ausbügeln, und riskieren einen Blick auf FP-Formate von CPUs. Zum Abschluss geht es jedoch in die Grafikkarten-Welt zurück. Dort versuchen wir eine Einschätzung zu geben, inwiefern Gleitkomma-Formate in zukünftiger Hardware eine Rolle spielen könnten.
Der Intel 8087 als ein externer Coprozessor für den 8086er Prozessor (welcher nur Integer-Berechnungen beherrscht) arbeitete mit heutigen CPUs verglichen zwar sehr langsam, eine Division benötigte über 200 Takte. Dennoch, verglichen mit "Software-Floating-Point" (also einem Unterprogramm, welches FP-Rechnungen auf einer reinen Integer-CPU "von Hand" ausführt) war das rasend schnell, um etwa zwei Größenordnungen schneller (setzt man eine Größenordnung mit Faktor 10 an, meinen wir mit zwei Größenordnungen Faktor 100).
Damit wurde auf der PC-Schiene der Weg für Floating-Point-Zahlen überhaupt erst frei gemacht. Auch Z80-basierende Computer konnten zwar mit Fließkomma-Zahlen umgehen. Da der Z80-Chip selbst aber nur auf Integer-Basis rechnet, musste das Betriebssystem hierfür Software-Funktionen zur Verfügung stellen. Für den Home-Bereich war das zu dieser Zeit ausreichend, die Ausführung eines solchen Befehls war so schnell, dass man bei der Ausführung keine Verzögerung wahrnahm. Jegliche wirtschaftlich orientierten Einsätze, bei denen ständig FP-Rechnungen anstehen, waren damit aber nicht zufriedenstellend realisierbar.
3D-Rendering ist teilweise mit zweckentfremdeten Integer-Zahlen möglich. Einige Algorithmen können Polygone füllen und nutzen dafür lediglich die x86-Register (Integer) und -Rechnungen (ebenfalls Integer). Auch bei GPUs drückte man sich solange es ging um FP-Rechenwerke, da sich Integer- bzw. Fixpoint-Units mit viel weniger Transistoren realisieren lassen. FP-Zahlen sind in einigen Zahlenbereichen bekanntlich ungenauer, als Fixpoint-Zahlen gleicher Bitlänge. Jedes FX-Format hat aber eine lineare Quantisierung, wodurch kleine Zahlenwerte relativ gesehen ungenauer gespeichert werden, als große.
FP ermöglicht, seine Genauigkeit über einen sehr viel größeren Bereich auszuspielen. Das ist für viele Rechnungen von Vorteil. Anders formuliert: Mit Fixpoint kann man sich solange aushelfen, sofern die Zwischen- und Endergebnisse alle in einem bekannten und recht eng begrenztem Zahlenbereich sind. Sofern sich nicht absehen lässt, wie groß oder klein die Zahlen werden, bzw. wenn man sowieso eine hohe Dynamik braucht, führt kein Weg um das Gleitkomma herum.
Mit dem 8087 wurde nun die PC-Schiene FPU-mäßig bedient. Dabei handelte es sich aber um Flickwerk, der 8086 und dessen Billig-Version 8088 waren nicht direkt für den Einsatz mit einem Coprozessor designt. Der 8087 hatte dann auch eine recht umständliche Art, die Register anzusprechen. Das war nicht direkt möglich, sondern nur über einen Stack ("Stapelspeicher"), was die Benutzung erschwerte. Die Funktionialität selbst wurde über ein Interrupt verfügbar gemacht.
Obwohl alle diese Umstände keinen Eindruck von überragender Performance hinterlassen, bot die direkte Hardware-Integration von FP-Rechnungen gegenüber Software-Bibliotheken, die FP-Rechnungen emulieren, doch gewaltige Geschwindigkeitsvorteile. Außerdem ist die CPU bei der FP-Emulation voll beschäftigt, während der Einsatz einer FPU ermöglicht, dass sich die CPU während der FP-Berechnungen anderen Aufgaben widmet. Dazu sind die Rechnungen natürlich zu synchronisieren, was Signalprocessing und -Handling erforderlich macht, und wenn man viele FP-Rechnungen in Folge hat, muss die CPU auf jeden Fall warten.
Die abgesetzten Stückzahlen des 8087 erreichten nie auch nur annähernd den Umfang der Verkäufe der Haupt-CPUs. Der mathematische Coprozessor blieb sehr teuer (was sich übrigens bis zum 80387 so hielt). Das rief die Konkurrenz auf den Plan, die pinkompatible Chips auf den Markt brachte. Einige Hersteller entwickelten die internen Schaltungen weiter. Die Intel-Version war relativ schlecht optimiert, ganz offensichtlich stand bei Intel im Vordergrund, überhaupt erst einmal die Funktionalität anbieten zu können.
Bishin zum 80186 war nicht klar, welche CPU-Schiene sich beim Privatmann durchsetzen würde. Intel baute dann eine bessere CPU (ebenfalls noch auf auf 16-Bit-Basis), den 80286. Dieser bot Leistung auf bislang unbekanntem Level, das Sterben der "Home-Computer" auf Z80-Basis bzw. der kleinen Motorola-Chips war nun endgültig besiegelt. Wolfenstein 3D, welches 3D-Grafik (auf sehr einfachem Level) bot, wäre ohne den 286er nicht denkbar gewesen. Auch Wing Commander (mit aus heutiger Sicht ebenfalls primitiver Grafik) verschlang viel Leistung. Richtige 3D-Spiele wie "LHX Attack Chopper" brauchten sogar einen 386er für flüssige Animationen, denn der Leistungshunger von 3D-Anwendungen ist schier unersättlich. Die hier genannten Spiele setzten allerdings keine FPU voraus, da sich nur die wenigsten Spieler einen mathematischen Coprozessor gönnten.
Mit dem 80486 wurde der Coprozessor auf der PC-Schiene gleich in die Haupt-CPU integriert. Seitdem steht nennenswerte FP-Leistung auch dem normalen Anwender zur Verfügung. Gegenüber dem Gespann 386+387 ergaben sich durch die Integration weitere Vorteile bezüglich der Latenzen, da sich die Wartezeiten minimieren lassen. Die FP-Rechenwerke durchliefen eine ständige Entwicklung, praktisch kein Coprozessor ist hundertprozentig mit den anderen kompatibel.
Das macht dem Programmierer, der Hochsprachen verwendet, natürlich nichts aus: Der Compiler kümmert sich darum. Die FP-Einheiten wurden für den Pentium neu auf Geschwindigkeit getrimmt, diese greifen jetzt bei bestimmten Rechnungen auf vorberechnete Tabellen zurück. Einige Werte darin waren in den ersten Pentiums falsch - unter Umständen war das Rechenergebnis ungenau. Mehr oder weniger schlimme Bugs sind in CPUs allerdings Gang und Gäbe - neben diesem FDIV-Bug war der F0-Bug präsent: Bestimmte Bit-Folgen (die alle mit hexadezimal F0 beginnen) führten dazu, dass die CPU still stand und neu gebootet werden musste, Speicherschutz hin oder Multitasking her.
Es bleibt festzuhalten, dass Floating-Point-Rechungen auf direkten Hardware-Support angewiesen sind, da man sie nur mit sehr hohem Aufwand emulieren kann. Das ist mit Echtzeit-3D-Rendering vergleichbar, welches natürlich auch rein softwaremäßig von der CPU gemacht werden kann - doch Freude respektive einer flüssigen Bilderfolge kommt erst mit dedizierter Hardware auf.
Real - Floating-Point in Software
Borland führte mit Turbo Pascal einen FP-Datentyp ein, der die krumme Länge von 48 Bit hat. "Real" (sprich: "Riehl") war speziell dafür gedacht, auf Rechnern verwendet zu werden, die keinen Coprozessor haben - alle Real-Rechnungen übernahmen mitgelieferte Software-Bibliotheken. Dabei wird bei "Real" eine Mantisse von 39 Bit verwendet, wodurch die Rechnungen gegenüber "Single" immerhin 65536mal genauer werden.
Allerdings bleibt der maximale Werte-Bereich vergleichbar mit dem, was FP32 bietet: Auch Real nutzt nur einen 8-Bit-Exponenten. Damit ergibt sich eine absurde Situation, was die Genauigkeit bei sehr kleinen Zahlen betrifft: Das Single-Format einer CPU unterstützt Denorms, Real aus Performance-Gründen aber nicht. Obwohl Single die viel kleinere Mantisse hat, kann man mit diesem Format näher an die Null kommen, ohne dass der Zahlenwert auf Null abgeschnitten wird. Doch sobald man im Bereich arbeitet, wo der Exponent noch hinreicht, ist Real gegenüber Single sehr viel genauer, da die zusätzlichen Bits in der Mantisse ausgespielt werden können.
Wie schon bei FP32 vs. FP24 besprochen, könnte ein zusätzliches Bit im Exponenten Wunder wirken, wenn keine Denorms unterstützt werden. Es scheint unverständlich, dass man sich bei Real nicht mit 38 Bit für die Mantisse zufrieden gab, um dem Exponenten 9 Bit spendieren zu können. Vielleicht war aber gerade dieser "Grenzübertritt" dafür verantwortlich, denn 8 Bit passen noch in das kleinste Register einer x86-er CPU.
Heutzutage ist "Real" ohne praktische Relevanz, da in jedem auch nur halbwegs modernen PC eine FP-fähige Hardware zur Verfügung steht. Man muss für heutige Compiler explizit "Real48" verlangen - kompiliert man einen Source Code, der noch "Real" fordert, bekommt man "Double". Auch im Grafik-Bereich ist es erlaubt, genauer zu rechnen, als die Definition fordert: So rechnet die Radeon ab 9500 selbst Pixelshader 1.1 schon mit FP24 (statt der Minimal-Forderung FX9 oder der Empfehlung FX12), während die GeForceFX bei DirectX9-Shadern für Full Precision FP32 bietet (statt FP24).
Doch zurück zu Turbo Pascal und dem Real-Typ: "Real" steht für "reelle Zahlen", dabei können Floating-Point-Formate grundsätzlich nur "rationale" Zahlen darstellen. Eine rationale Zahl zu haben, heißt nicht, dass sie vernünftig ist :-), sondern dass sie sich mit einem Bruch darstellen lässt. Alle Floating-Point-Zahlen lassen sich als Bruch darstellen. In der Mathematik kennt man auch irrationale Zahlen, die Quadratwurzel aus 2 gehört zum Beispiel dazu. Solche Werte sind nicht als Bruch (mit jeweils rationalem Zähler und Nenner) darstellbar. Pi ist übrigens nicht nur irrational, sondern sogar transzendent, das ist die "höchste" Stufe.
Unter reellen Zahlen, was der "real"-Typ vom Namen her eigentlich sein müsste, versteht man sowohl rationale als auch irrationale. Wie gesagt sind Floating-Point-Zahlen aber immer rational. Irrationale Zahlen ließen sich einem FP-Format schon deshalb nicht zuweisen, weil man ja erst mal alle Nachkomma-Stellen angeben müsste - und irrationale Zahlen haben unendlich viele Nachkomma-Stellen, die zudem keine Regelmäßigkeiten aufweisen. Selbst wenn das möglich wäre, würde bei der Zuweisung einfach auf eine in der Nähe liegende rationale Zahl gerundet, die sich mit dem gewählten FP-Format noch darstellen lässt.
Dennoch kann man durchaus mit Werten rechnen, die durch Gleitkommazahlen noch repräsentierbar sind, da für die meisten Anwendungen eine begrenzte Genauigkeit ausreicht. In der Mathematik gibt es ein extra Gebiet "Numerik", welches sich unter anderem mit Fehler-Rechnungen befasst: Mit Näherungsformeln lässt sich im Vorhinein abschätzen, wie groß die Rechenfehler bei einer gegebenen Zahlen-Genauigkeit werden. Dann vergleicht man das mit den Anforderungen der Applikation und nimmt gegebenenfalls ein besseres Format.