                           QuickFloat
               Eine 16-bit Floating Point Implementation
                              oder
                  Der elektronische Rechenschieber


Das Rechnen mit Fliekommazahlen ist fr jeden Computer eine recht aufwendige Anglegenheit. Sofern er keine spezielle Hardware dafr hat (Arithmetik - Coprozessor), mu die Arbeit zeitraubend in Software erledigt werden. Schon die vier Grundrechenarten Addition, Subtraktion, Multiplikation und Division erfordern umfangreiche Shifts und Bitmanipulationen am Exponenten- und Mantissen-Teil ihrer Argumente [1].

Oft lt sich ein derartiger Aufwand nicht rechtfertigen. Man denke nur an 8-bit Mikrocontroller-Applikationen oder an schnelle Spiele auf Heimcomputern. Auch bei zeitkritischen MS-DOS Anwendungen kann im allgemeinen Fall nicht mit der Anwesenheit eines Rechenknechtes gerechnet werden.

Man kann sich zwar in vielen dieser Flle mit sorgfltig scalierter Integer - Arithmetik behelfen, doch oft erzwingt der Wertebereich (Dynamik) der mglichen Zahlenwerte die Verwendung von Fliekommazahlen, obwohl deren Genauigkeit eigentlich mit 16 Bit (anstelle der 32 oder 64 Bit von Fliekommazahlen) befriedigt werden knnte.

Im Folgenden wird eine Methode beschrieben, die durch logarithmische Zahlenreprsentation Fliekommaoperationen auf Integeroperationen zurckfhrt und dabei auf herkmmlicher Hardware in den Geschwindigkeitsbereich eines Floatingpoint-Prozessors kommt.

Sie entstand im Rahmen eines Modell-Flug-Simulators, an dem ich zur Zeit arbeite, und hat sich dabei ausgezeichnet bewhrt. So ein Simulator ist ein typisches Beispiel fr eine Anwendung, bei der es mehr auf hohe Rechengeschwindigkeit und Dynamik der Rechengren ankommt als auf Przision der Ergebnisse: Schlielich ist es ziemlich egal, ob das Flugzeug auf dem Bildschirm einen Pixel weiter links oder rechts fliegt, solange es sich dabei ausreichend realistisch verhlt.

Die vorgestellte Implementation ist zum grten Teil in 8086-Assembler geschrieben und als C-Library konzipiert, knnte aber auch ganz in C oder einer anderen Sprache realisiert werden.
Auer den Grundrechenarten sind Routinen und Makros fr Negation, Absolutwerte, Wurzeln, Potenzen, Sinus, Cosinus, Zahlenvergleiche, Ganzzahlen- und String-Konvertierungen vorhanden.


                           Grundlagen

Vor mehr als einem Jahrzehnt verffentlichte Kenneth A. Simons einen Artikel [2], in dem er eine universelle Notation auf der Basis von Logarithmen - den "N-Log" - zur Zahlendarstellung vorschlgt.

Die Idee is frappierend einfach und elegant: Rechnet man anstelle von Zahlen mit ihren Logarithmen, vereinfachen sich viele Rechenoperationen in erheblichem Mae. Wie wir (hoffentlich) noch aus dem Mathematikunterricht wissen, gilt

   log(x*y) = log(x) + log(y)

d.h. also, anstatt zu multiplizieren, brauchen wir die Logarithmen der Zahlen nur zu addieren. Wer sich noch an den guten alten Rechenschieber erinnert, wei, da er genau nach diesem Prinzip funktioniert.
In hnlicher Weise lassen sich Potenzen auf Multiplikationen zurckfhren:

   log(x**y) = y * log(x),

und Wurzeln auf Divisionen etc. Jene umfangreichen Bnde mit Logarithmen-Tabellen aus der Pr-Taschenrechner-Zeit verdanken ihre Existenz vor allem dieser Tatsache.

Leider hat die Sache auch einen Schnheitsfehler, der einiges an Kopfzerbrechen bereitet: Addition und Subtraktion von Logarithmen sind etwas umstndlich zu handhaben, doch dazu spter mehr.

Simons definiert

   "The N-log of a number is that integer most closely approximating the
   product of EXP N and the LOG of the number",

wobei er immer zur Basis 10 rechnet, also

   N-log(x) = round(10**N * log10(x))

Das bedeutet nichts anderes, als da der Logarithmus der betreffenden Zahl durch Skalierung und anschlieende Rundung zu einer ganzen Zahl gemacht wird.
Damit wird beispielsweise

   - der 3-log von 986e-12  zu 10**3 * -9.006  gleich -9006
und
   - der 2-log von 123e10   zu 10**2 * 12.09  gleich 1209.

Der Knackpunkt ist also, da sich jede darstellbare Zahl (auch dazu spter mehr) - egal ob gro oder klein - durch eine Integer darstellen lt.

Will man zwei dieser Zahlen miteinander multiplizieren, so gengt im Prinzip ein einfacher und blitzeschneller "Add" Maschinenbefehl.

Wie steht es nun aber mit Addition und Subtraktion? Simons beschreibt einen Algorithmus (nach Leonelli und Gauss), mit dem log(x+y) und log(x-y) direkt gefunden werden knnen, wenn log(x) und log(y) bekannt sind. Mit ihm lassen sich Lookup-Tabellen erstellen, in denen die gewnschten Resultate zur Laufzeit schnell und effektiv ermittelt werden knnen. Besonders dieser Punkt macht den obengenannten Artikel wertvoll und interessant. Gleichzeitig beschrnkt er aber leider auch die universelle Anwendbarkeit der gesamten Methode, weil de Gre dieser Tabellen mit der Rechengenauigkeit ansteigt.

Je grer man das N in der obigen Definition whlt, umso mehr erhht sich die Genauigkeit der Zahlendarstellung: Whrend beispielsweise 2-logs eine Przision von 1.15% besitzen, kommen 3-logs auf 0.115%, 4-logs auf 0.0115% usw. Die fr Addition und Subtraktion bentigten Tabellen besitzen fr 2-logs jeweils etwa 194 Eintrge, fr 3-logs 2938 und fr 4-logs bereits 39387 Eintrge. Damit wrden bei 16 Bit Wortbreite von den zwei Tabellen rund 154 kByte belegt, wobei auch mit 0.0115% wahrhaftig noch keine berauschende Genauigkeit erreicht wird: Schon die 23-bit Mantisse der 32-bit "Short" Float weist eine Przision von 0.0000119% auf; hier wrde man in die Grenordnung eines halben Gigabytes fr die Tabellen kommen.

Begngt man sich aber mit einer geringeren Rechengenauigkeit, lt sich eine schnelle und relativ platzsparende Addition/Subtraktion implementieren.


                           QuickFloats

Nach eigenen Angaben sieht sich Simons - zumindest zur Zeit der Verffentlichung des oben erwhnten Artikels - nicht in der Lage, sein N-logs-Konzept selbst zu verwirklichen, und bittet interessierte Leser um Hilfe. Er bietet zwar einen konkreten Implementationsvorschlag (auf der Basis von 3-logs) an, ich mchte diesen hier aber nicht weiter verfolgen, weil er mir aus verschiedenen Grnden ungnstig erscheint.

QuickFloats, so wie sie sich in den folgenden Listings manifestieren, verwenden als Logarithmen-Basis 256 anstelle von 10. Dies bietet gewisse Vorteile bei Konvertierungen von/nach Integer.

Jede QuickFloat-Zahl wird durch 16 Bits reprsentiert. Da der Logarithmus nur fr positive Zahlen definiert ist, mu ein Bit zur Kennzeichnung negativer Zahlen reserviert werden. Diese Funktion bernimmt Bit 0 in unserem Falle, es bleiben also die oberen 15 Bits zur Aufnahme des absoluten Zahlenwertes in (vorzeichenbehafteter) Zweikomplementdarstellung. "Vorzeichenbehaftet" deswegen, weil der Logarithmus von Zahlen kleiner als Eins bekanntlich negativ, und von Zahlen grer als Eins positiv ist.

Mit N als frei whlbarer Konstanten errechnet sich der Zahlenwert zu

   qflt(x) = N * log256(x)

oder (nach der Basen-Umrechnungsformel fr Logarithmen)

   qflt(x) = N * ln(x)/ln(256)

Whlt man N gleich 4000 (wie es auch im oben erwhnten Flugsimulator geschah), so lassen sich mit diesen 15 Bit die Zahlen von 1.37e-10 bis 7.30e+9 darstellen, mit einer Przision von 0.069%.
Bentigt man eine sehr groe Dynamik im Wertebereich, so knnte man mit N = 200 den Bereich 5.36e-198 bis 1.87e+197 abdecken, mu sich aber mit einer Przision von 1.4% zufriedengeben.
Ein realistischer Wert wre beispielsweise auch mit N = 1000 gegeben, mit 0.28% im Bereich 3.51e-40 bis 2.85e+39.

Fr eine gegebene Anwendung ist es also ratsam, zuerst die Extremwerte der bei den vorgesehenen Berechnungen (und Zwischenergebnissen!) auftretenden Werte abzuschtzen und dann das entsprechende N zu whlen, weil so die optimale Przision gewhrleistet wird.

Weiterhin wird im QuickFloat-Format die kleinste mit 16 Bit darstellbare Zahl, die -32768, als "Null" definiert. Ein spezieller Wert fr Null hat sich als unumgnglich erwiesen, der leider auch zur Laufzeit bei verschiedenen Rechenoperationen immer mit berprft werden mu. Es treten sonst wundersame Effekte auf, die Zahlenberlufe bewirken oder auf kumulativem Wege vollkommen unrealistische Werte hervorbringen knnen.



                           Tabellenerzeugung


                           Die Implementation


                        Anmerkungen und Ausblick

Ideal wre eine Hardware-Implementation, die sicher viel kostengnstiger als konventionelle Floatingpoint-Hardware ausfiele.


                           Quellenangaben

[1] Donald E. Knuth
   The Art of Computer Programming
   Vol.2 (Seminumerical Algorithms), S.198 ff.
[2] Keneth A. Simons
   N-Logs: A New Number Language for Scientific Computers
   Dr.Dobb's Journal of Computer Calisthenics & Orthodontia
   November/December 1980, S.4


******
Simons:
   Schlgt N-logs als allgemeinen Ersatz fr Exponentialschreibweise vor,
      Ein-/Ausgaben ebenfalls nur N-logs
   Keine eigene Implementation
   Basis 10 ungnstig
   Nichtbeachtung der Besonderheit der NULL
   Keine Konvertierung von/zu Integerzahlen vorgesehen
   "Genauigkeit von 0.115% reicht fr die meisten wissenschaftlichen
      Berechnungen aus"

   Mgliche Verbesserungen und Erweiterungen
      Error Check fr Div/0
      Flexibles Ausdrucken von Qflt's ("printf")

   Auerdem nehmen die Fliekommazahlen selber im Speicher ziemlich viel
   Platz ein (mindestens 32 bit pro Zahl).
