Fließkommaarithmetik
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.
Inhaltsverzeichnis
Funktionsweise[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]
Grundsätzlich werden die Fließkommazahlen in 2 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]
Zwei Bereiche in der Zeropage sind für das Rechnen mit Fließkommazahlen reserviert:
- Der erste ist der sogenannte FAC (Floating Point ACcumulator), gelegentlich auch als FAC1 bezeichnet:
- 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: 0 für positive und $FF (-1) für negative Zahlen.
- Adresse 112/$70 enthält Rundungsbits der Mantisse bei Zwischenberechnungen.
- Der zweite ist das sogenannte ARG (Floating Point ARGument), gelegentlich auch FAC2 oder AFAC (Alternate FAC) genannt. Er ist im Wesentlichen wie der FAC bzw. FAC1 angelegt:
- 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 MSB: 0 für positive und $FF (-1) für negative Zahlen.
Eine Fließkommazahl im FAC belegt 7 und im ARG belegt 6 Bytes.
Variablendarstellung[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:
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]
- 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]
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]
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]
Name | Adresse dez. |
Adresse hex. |
Beschreibung |
---|---|---|---|
CONUPK | 47756 | BA8C | ARG mit Zahl aus dem Speicher füllen (A=Adr.LB, Y=Adr.HB) |
MOVEF | 48143 | BBFC | Zahl in ARG nach FAC kopieren |
MOVFA | 48124 | BC0F | 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]
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]
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) |
FADDT | 47210 | B86A | Addiert ARG zu FAC |
FDIV | 47887 | BB0F | Teilt Zahl in RAM durch FAC (A=Adr.LB, Y=Adr.HB) |
FDIVT | 47890 | BB12 | Teilt ARG durch FAC, Vorzeichen von FAC wird ignoriert und stets als positiv angenommen. |
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) |
FPWR | 49016 | BF78 | Potenziert ARG mit Zahl aus RAM (A=Adr.LB, Y=Adr.HB) |
FPWRT | 49019 | BF7B | Potenziert ARG mit FAC |
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 | 57411 | E043 | Polynomauswertung für FAC. |
POLY2 | 57433 | E059 | Polynomauswertung nur mit ungeraden Exponenten für FAC |
SIN (ROM-Routine) | 57963 | E26B | Funktion SIN auf FAC anwenden |
SGN (ROM-Routine) | 48185 | BC39 | Funktion SGN 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]
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]
Wikipedia: Gleitkommazahl |
Wikipedia: Exzesscode |
- Microsoft BASIC for 6502: float routines, Source Repository on GitHub
- Project64: Memory-Mapping mit Fließkommaspeicherstellen und -routinen