Fließkommaarithmetik
Dieser Artikel beschreibt die interne Darstellung von und den Umgang mit Fließkommazahlen seitens des BASIC V2 des C64. Zu Fließkommazahlen aus Sicht der BASIC-Programmierung siehe Artikel Variable. |
Fließkommaarithmetik ist eine Methode zur Darstellung und Handhabung eines weiten Bereichs von Realzahlen in binärer Form. Der eingebaute BASIC-Interpreter des C64 enthält eine Reihe von Unterprogrammen für die verschiedensten Berechnungen mit Fließkommazahlen (auch Gleitkomma- oder Gleitpunktzahlen genannt). Diese Routinen können auch aus vom Benutzer erstellten Maschinenspracheprogrammen heraus aufgerufen werden, um Berechnungen mit Realzahlen im Bereich von ±2.93873588·10−39 bis ±1.70141183·1038 durchzuführen.
Funktionsweise[Bearbeiten | Quelltext bearbeiten]
Eine Realzahl T im Fließkommaformat besteht aus einer Mantisse m und einem Exponenten E, welche so "ausgewählt" werden, dass T = m · 2E entspricht.
Die Mantisse ist normalisiert, d.h. eine Festkommabinärzahl im Bereich von 0,5 bis 1, so dass 0,5 ≤ m < 1 gilt, d.h. unmittelbar nach dem Komma steht stets eine 1, dahinter folgen mehrere (31 im Falle des vom C64-BASIC verwendeten Formats) binäre Nachkommastellen.
Der vorzeichenbehaftete Exponent ist eine 8-Bit-Ganzzahl in Exzesscodierung, um negative Exponenten darstellen zu können, die für Fließkommazahlen mit einem Betrag < 0,5 oder >= 1 benötigt werden. Der Wert 128 (entspricht dem Exzess bzw. engl. bias) entspricht dabei einem Exponenten von 0, größere Werte stellen einen entsprechend großen positiven und kleinere einen negativen Exponenten dar. Der Vorteil davon liegt in der einfachen Vergleichbarkeit zweier Werte, ohne eine Subtraktion durchführen zu müssen, auch wenn beim Rechnen mit Exponenten mitunter Korrekturen nötig sind.
Wert | Bedeutung als Exponent | Fließkommawert |
---|---|---|
$00 | --- | 0 |
$01 | -127 | 2↑(-127) * Mantisse |
. . . | ||
$80 | 0 | 2↑0 * Mantisse |
. . . | ||
$FF | +127 | 2↑127 * Mantisse |
Ein separates Vorzeichenbit zeigt an, ob die aus Mantisse und Exponent resultierende Fließkommazahl positiv oder negativ ist. Damit wird der gesamte oben genannte Wertebereich abgedeckt, bis auf die 0: Da sich diese aufgrund der Wertebereichseinschränkungen der Mantisse mit o.g. Formel nicht darstellen lässt, signalisiert stattdessen ein Exponent mit dem Wert 0 (entspräche Exponent −128), dass der gesamte Fließkommaausdruck dem Wert 0 entspricht, unabhängig von der zugehörigen Mantisse.
Fließkommadarstellung auf dem C64[Bearbeiten | Quelltext bearbeiten]
Zu Fließkommazahlen aus Sicht der BASIC-Programmierung siehe Artikel Variable.
Grundsätzlich werden die Fließkommazahlen vom BASIC-Interpreter intern in zwei unterschiedlichen Formen abgelegt:
- In einer für Berechnungen praktikableren Form als Register bestehend aus 6 oder 7 Bytes.
- In Variablen in einer kompakten 5 Bytes umfassenden Form.
Registerdarstellung[Bearbeiten | Quelltext bearbeiten]
Zwei Bereiche in der Zeropage sind für das Rechnen mit Fließkommazahlen reserviert.
FAC[Bearbeiten | Quelltext bearbeiten]
FAC (Floating Point ACcumulator), gelegentlich auch als FAC1 bezeichnet, belegt sieben Bytes ab Adresse 97/$61.
- Adresse 97/$61 enthält den Exponenten,
- Adressen 98–101/$62–$65 enthalten die vier Bytes (32 Bit) der Mantisse,
- Adresse 102/$66 enthält das Vorzeichen im Most Significant Bit: %0xxxxxxx (gelöschtes Vorzeichenbit) für positive und %1xxxxxxx (gesetztes Vorzeichenbit) für negative Zahlen,
- Adresse 112/$70 enthält Rundungsbits der Mantisse bei Zwischenberechnungen.
ARG[Bearbeiten | Quelltext bearbeiten]
ARG (Floating Point ARGument), gelegentlich auch FAC2 oder AFAC (Alternate FAC) genannt, belegt sechs Bytes ab Adresse 105/$69. ARG ist im Wesentlichen wie FAC angelegt, beinhaltet aber kein Byte für Rundungsbits.
- Adresse 105/$69 enthält den Exponenten,
- Adressen 106–109/$6A–$6D werden von der Mantisse belegt,
- Adresse 110/$6E enthält das Vorzeichen im Most Significant Bit: %0xxxxxxx (gelöschtes Vorzeichenbit) für positive und %1xxxxxxx (gesetztes Vorzeichenbit) für negative Zahlen.
Variablendarstellung[Bearbeiten | Quelltext bearbeiten]
Um den Speicherverbrauch zu reduzieren, werden Fließkommazahlen im restlichem Arbeitsspeicher auf 5 Bytes komprimiert: Da wegen der Normalisierung der Mantisse das führende Bit stets 1 ist, wird das dafür benötigte Bit von den ROM-Routinen, welche Zahlen aus FAC oder ARG in den Arbeitsspeicher kopieren, durch das Vorzeichenbit ersetzt. Die Routinen, welche in umgekehrter Richtung kopieren, lagern das Vorzeichenbit wieder aus und fügen das führende 1-Bit der Mantisse wieder hinzu.
Bezeichnung: | exp | sign/ma4 | ma3 | ma2 | ma1 | |||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Byte: | 0 | 1 | 2 | 3 | 4 | |||||||||||||||||||||||||||||||||||
Bit: | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
E | E | E | E | E | E | E | E | S | M | M | M | M | M | M | M | M | M | M | M | M | M | M | M | M | M | M | M | M | M | M | M | M | M | M | M | M | M | M | M |
Typische Werte in der Variablendarstellung (das MSB der Mantisse, wäre an der Stelle, wo sich das Vorzeichen befindet, ist aber immer 1 und muss zur Bildung des Wertes 2^e * M gedanklich mit eingesetzt werden, es entspricht dem Dezimalwert 0,5):
Exponent (hex.) |
Mantisse (hex.) |
Wert (dez.) |
---|---|---|
00 | xx xx xx xx | 0 (Mantisse egal) |
01 | 00 00 00 00 | 2.93873588E-39 (betragsmäßig kleinster Wert) |
80 | 00 00 00 00 | 0.5 |
81 | 00 00 00 00 | 1 |
81 | 80 00 00 00 | -1 |
FF | 7F FF FF FF | 1.70141183e+38 (betragsmäßig größter positiver Wert) |
FF | FF FF FF FF | -1.70141183e+38 (betragsmäßig größter negativer Wert) |
Umrechnungsbeispiel[Bearbeiten | Quelltext bearbeiten]
- Exponent: exp-128
- Mantisse: (m4 >= 128 ? -1 : +1) * ((m4 | 0x80) >> 8 + m3 >> 16 + m2 >> 24 + m1 >> 32)
als Ausdruck in der Syntax der Programmiersprache C, wobei "x >> y" as x*2↑(-y) zu verstehen ist (Bit-Schiebeoperation nach rechts).
exp m4 m3 m2 m1 98 35 44 7A 00 - Hexadezimaldarstellung 152 53 68 122 0 - Dezimaldarstellung 10011000 00110101 01000100 01111010 00000000 - Binärdarstellung ^sign bit In diesem Fall: Exponent = 152 - 128 = 24 ; dez Mantisse = 0.10110101010001000111101000000000 ; bin Mantisse = +1 * (53 >> 8 + 68 >> 16 + 122 >> 24 + 0 >> 32) ; dez = 1 * 2^-1 + 0 * 2^-2 + 1 * 2^-3 + 1 * 2^-4 + 0 * 2^-5 + ... = 0.70807611942291259765 Die Zahl ist also 0.70807611942291259765 * 2^24 = 11879546.0
Verwendung in Basic[Bearbeiten | Quelltext bearbeiten]
In sämtlichen CBM-Basic-Varianten werden Ausdrücke grundsätzlich in Fließkommaarithmetik durchgeführt, selbst dann, wenn nur einfachere Datentypen, wie etwa Ganzzahlvariablen beteiligt sind.
Abgesehen von den üblichen Operatoren gibt es folgende besondere Funktionen in Bezug auf Fließkommazahlen bzw. deren Eigenschaften:
- ABS - den Betrag einer Fließkommazahl ermitteln (etwaiges negatives Vorzeichen entfernen)
- INT - die nächstkleinere Ganzzahl ermitteln (Nachkommastellen entfernen).
- SGN - das Vorzeichen ermitteln
- STR$ - eine Fließkommazahl in eine Zeichenkette verwandeln
- VAL - eine Zeichenkette in eine Fließkommazahl wandeln
Nutzung von Fließkommaroutinen[Bearbeiten | Quelltext bearbeiten]
So wie praktisch alle arithmetischen Maschinensprachebefehle den Akkumulator der CPU nutzen, stellen FAC und ARG die Dreh- und Angelpunkte für Fließkommaberechnungen dar: Die zu verarbeitenden Zahlen werden in FAC oder ARG kopiert und von den per JSR aufrufbaren Fließkommaroutinen im ROM gemäß deren Funktion manipuliert. Das Ergebnis der Operation steht anschließend im FAC bereit.
Falls Fließkommazahlen im Arbeitsspeicher für die Operation herangezogen oder aus FAC oder ARG dorthin kopiert werden, ist deren Adresse an die ROM-Routinen per A-, X- und/oder Y-Register zu übergeben, wobei die genaue Verwendung der Register je nach Funktion variiert. Auch muss man bei den meisten Rechenoperationen damit rechnen, dass FAC und ARG überschrieben werden.
Verschiebe-/Kopierroutinen[Bearbeiten | Quelltext bearbeiten]
Name | Adresse dez. |
Adresse hex. |
Beschreibung |
---|---|---|---|
CONUPK | 47756 | BA8C | ARG mit Zahl aus dem Speicher füllen (A=Adr.LB, Y=Adr.HB). Vergleicht danach noch als Vorbereitung für nachfolgende Operationen die Vorzeichen von ARG und FAC und schreibt das Ergebnis nach Adresse $6F ($00: Vorzeichen gleich, $80: Vorzeichen ungleich), und lädt den Exponenten von FAC nach A (d.h. setzt Zero Flag genau dann, wenn FAC gleich null). Die Routinen FADDT, FDIVT, FMULTT und FPWRT setzen diese Vorbereitung voraus. |
MOVAF | 48124 | BBFC | Zahl in ARG nach FAC kopieren |
MOVFA | 48140 | BC0C | Zahl in FAC nach ARG kopieren |
MOVFM | 48034 | BBA2 | FAC mit Zahl aus dem Speicher füllen (A=Adr.LB, Y=Adr.HB) |
MOVMF | 48084 | BBD4 | Zahl aus FAC im Speicher ablegen (X=Adr.LB, Y=Adr.HB) |
Formatumwandlungen[Bearbeiten | Quelltext bearbeiten]
Name | Adresse dez. |
Adresse hex. |
Beschreibung |
---|---|---|---|
FACINX | 45482 | B1AA | Zahl in FAC zu 16-Bit-Ganzzahl mit Vorzeichen wandeln (Y=LB, A=HB). |
FIN | 48371 | BCF3 | Nullterminierten PETSCII-String zu Fließkommazahl in FAC wandeln (Stringadresse in $7A/$7B). Erwartet erstes Zeichen im Akku und gelöschtes Carry-Flag, z.B. durch Aufruf von CHRGOT (JSR $0079). |
STRVAL | 47029 | B7B5 | PETSCII-String zu Fließkommazahl in FAC wandeln (String-Adresse in $22/$23, String-Länge im Akku). |
FOUT | 48605 | BDDD | Zahl in FAC zu nullterminiertem PETSCII-String wandeln (ab $0100 ff., Adresse auch in A, Y). Direkte Ausgabe des FAC-Inhalts als String ist auch über $AABC/43708 möglich. |
GIVAYF | 45969 | B391 | 16-Bit-Ganzzahl mit Vorzeichen zu Fließkommazahl in FAC wandeln (Y=LB, A=HB). |
QINT | 48283 | BC9B | Zahl in FAC in 32-Bit-Ganzzahl mit Vorzeichen wandeln ($62-$65, Big-Endian-Reihenfolge). |
Rechenroutinen[Bearbeiten | Quelltext bearbeiten]
Name | Adresse dez. |
Adresse hex. |
Beschreibung |
---|---|---|---|
ABS (ROM-Routine) | 48216 | BC58 | Funktion ABS auf FAC anwenden |
ATN (ROM-Routine) | 58126 | E30E | Funktion ATN auf FAC anwenden |
COS (ROM-Routine) | 57956 | E264 | Funktion COS auf FAC anwenden |
MUL10 | 47842 | BAE2 | Zahl in FAC mit 10 multiplizieren |
DIV10 | 47870 | BAFE | Zahl in FAC durch 10 teilen. Vorzeichen wird ignoriert, das Ergebnis ist stets positiv. |
EXP (ROM-Routine) | 49133 | BFED | Funktion EXP auf FAC anwenden |
FADD | 47207 | B867 | Addiert Zahl in RAM zu FAC (A=Adr.LB, Y=Adr.HB) |
FADDH | 47177 | B849 | Addiert 0.5 zu FAC - nützlich zum Vorbereiten einer nachfolgenden Rundung |
FADDT | 47210 | B86A | Addiert ARG zu FAC (Voraussetzungen beachten!) |
FDIV | 47887 | BB0F | Teilt Zahl in RAM durch FAC (A=Adr.LB, Y=Adr.HB) |
FDIVT | 47890 | BB12 | Teilt ARG durch FAC (Voraussetzungen beachten!) |
FINLOG | 48510 | BD7E | Akkumulator als vorzeichenbehaftete Ganzzahl zu FAC addieren |
FMULT | 47656 | BA28 | Multipliziert FAC und Zahl im RAM (A=Adr.LB, Y=Adr.HB) |
FMULTT | 47664 | BA30 | Multipliziert FAC mit ARG, wie FMULT, aber ohne Einlesen der Zahl nach ARG aus dem RAM - nützlich, wenn der 2. Faktor bereits in ARG präsent ist (Voraussetzungen beachten!) |
FPWR | 49016 | BF78 | Potenziert ARG mit Zahl aus RAM (A=Adr.LB, Y=Adr.HB) |
FPWRT | 49019 | BF7B | Potenziert ARG mit FAC (Voraussetzungen beachten!) |
FSUB | 47184 | B850 | Subtrahiert FAC von Zahl in RAM (A=Adr.LB, Y=Adr.HB) |
FSUBT | 47187 | B853 | Subtrahiert FAC von ARG |
INT (ROM-Routine) | 48332 | BCCC | Funktion INT auf FAC anwenden |
LOG (ROM-Routine) | 47594 | B9EA | Funktion LOG auf FAC anwenden |
NEGOP | 49076 | BFB4 | Ändert das Vorzeichen einer Zahl !=0 in FAC |
POLY | 57433 | E059 | Polynomauswertung für FAC |
POLYX | 57411 | E043 | Polynomauswertung nur mit ungeraden Exponenten für FAC |
ROUND | 48155 | BC1B | FAC runden (Rundungsbyte in Mantisse einarbeiten) |
SGN (ROM-Routine) | 48185 | BC39 | Funktion SGN auf FAC anwenden |
SIN (ROM-Routine) | 57963 | E26B | Funktion SIN auf FAC anwenden |
SQR (ROM-Routine) | 49009 | BF71 | Funktion SQR auf FAC anwenden |
TAN (ROM-Routine) | 58036 | E2B4 | Funktion TAN auf FAC anwenden |
Vergleichsoperationen[Bearbeiten | Quelltext bearbeiten]
Name | Adresse dez. |
Adresse hex. |
Beschreibung |
---|---|---|---|
FCOMP | 48219 | BC5B | Vergleicht FAC mit Zahl im Speicher (A=Adr.LB, Y=Adr.HB). Ergebnis in Statusregister und Akku: A=0: FAC=RAM, A=1: FAC>RAM, A=-1(255): FAC<RAM |
SIGN | 48171 | BC2B | Setzt Negative- und Zero-Flags im Statusregister entsprechend dem Vorzeichen und Betrag der Zahl in FAC |
Weblinks[Bearbeiten | Quelltext bearbeiten]
Wikipedia: Gleitkommazahl |
Wikipedia: Exzesscode |
- Microsoft BASIC for 6502: float routines, Source Repository on GitHub
- Project64: Memory-Mapping mit Fließkommaspeicherstellen und -routinen