FPWR

Aus C64-Wiki
Zur Navigation springenZur Suche springen

Anmerkung: Dieser Artikel beschreibt die numerische FPWR-Routine zur Berechnung von Potenzen im BASIC-ROM.

Name: FPWR
Beschreibung: Fließkommazahl aus dem Speicher nach FAC holen, ARG mit dieser Zahl potenzieren und Resultat in FAC speichern
Einsprungpunkt: $BF78 / 49016
Übergebene Argumente:
Rückgabe-Werte:


FPWR — manchmal auch als MEMPOT[1], HOCHMEM[2], M-POT[3] oder einfach als Potenzierung[4] bezeichnet — holt eine Fließkommazahl aus dem Speicher des C64 in das Fließkommaregister FAC und potenziert dann den bereits im Fließkommaregister ARG befindlichen Wert mit dieser Zahl. Die Speicheradresse der Fließkommazahl wird im Akkumulator (Low-Byte) und im Y-Register (High-Byte) übergeben. Die Zahl muss im kompakten 5 Byte-Format vorliegen, wie es zum Speichern von REAL-Variablen verwendet wird. Es können sowohl Werte im RAM als auch Konstanten im BASIC-ROM adressiert werden.

Nach dem Aufruf steht in FAC die errechnete Potenz, während der Inhalt von ARG undefiniert ist. Ist das Ergebnis der Berechnung zu groß für die Fließkommadarstellung des C64, so löst FPWR einen ?OVERFLOW ERROR aus. Falls versucht wird, eine negative Zahl mit einem nicht ganzzahligen Exponenten zu potenzieren, so meldet FPWR einen ?ILLEGAL QUANTITY ERROR.

Neben dem Inhalt der Fließkommaregister FAC und ARG ändert FPWR auch den Hilfszeiger an Adresse 34/$22 (Low-Byte) und 35/$23 (High-Byte) und die Hilfsregister an den Adressen 38/$26 bis 41/$29 sowie 78/$4E bis 82/$52 sowie die Fließkommaregister FAC#3 und FAC#4 an den Adressen 87/$57 bis 96/$60.

Algorithmus

FPWR implementiert keine eigenständige Berechnung der Potenz, sondern prüft nur auf eine Reihe von Sonderfällen und wendet dann nacheinander die Routinen LOG, FMULT und EXP auf die Fließkommaregister an. Die ROM-Routine FPWR basiert also auf der Berechnungsformel ab = exp(b × log(a)).

  1. Zunächst wird geprüft, ob der Exponent in FAC gleich 0 ist. In diesem Fall verzweigt FPWR direkt zur ROM-Routine EXP, berechnet also den Funktionswert EXP(0). Dies ist eine zwar speicherplatzsparende, aber sehr aufwändige Methode, um die Konstante 1 in das Fließkommaregister FAC zu kopieren.
  2. Ansonsten wird untersucht, ob die Basis in ARG gleich 0 ist. In diesem Fall liefert FPWR direkt den Wert 0 als Ergebnis.
  3. Sind sowohl Basis als auch Exponent ungleich 0, so sichert FPWR nun zunächst den Exponenten in das Hilfsregister an den Adressen 78/$4E bis 82/$52. Anschließend prüft FPWR das Vorzeichen der Basis. Ist diese negativ und ist der Exponent ganzzahlig, so setzt FPWR das Vorzeichen der Basis in FAC auf "positiv" und merkt sich das niederwertigste Byte des Exponenten auf dem Stack. In allen anderen Fällen bleibt die Basis unverändert und der Wert 0 wird auf dem Stack hinterlegt.
  4. Nun berechnet FPWR die Potenz ab gemäß der oben angegebenen Formel. Hierfür wird zunächst mittels der ROM-Routine LOG der natürliche Logarithmus der in FAC gespeicherten Basis berechnet. In dem bisher nicht betrachteten Sonderfall, dass die Basis negativ und der Exponent nicht ganzzahlig ist, führt dieser Schritt zu einem ?ILLEGAL QUANTITY ERROR.
  5. Als nächstes multipliziert FPWR den soeben berechneten Logarithmus mit dem im dritten Schritt gesicherten Exponenten. Bei einem sehr großen Wert des Exponenten kann dieser Schritt einen ?OVERFLOW ERROR auslösen.
  6. Ansonsten wird nun die gesuchte Potenz berechnet, indem FPWR die ROM-Routine EXP auf das soeben berechnete Produkt b × log(a) anwendet. Falls das Ergebnis dieser Berechnung zu groß ist für die Fließkommadarstellung auf dem C64, so kann auch dieser Schritt noch einen ?OVERFLOW ERROR melden.
  7. Zuletzt holt FPWR den im dritten Schritt auf dem Stack gesicherten Wert zurück und überträgt dessen niederwertigstes Bit ins Carry-Flag. Ist dieses anschließend gesetzt, so wurde eine negative Basis mit einem ganzzahligen, ungeraden Exponenten potenziert und das Vorzeichen des Resultats muss noch negiert werden. Im Fall eines gelöschten Carry-Flags war entweder die Basis positiv oder der Exponent gerade, und keine weiteren Rechenschritte sind erforderlich.

Laufzeitverhalten

  • In jedem Fall wird zunächst von der Routine MOVFM der Exponent aus dem Speicher geholt (typischerweise 76, in Sonderfällen bis zu 80 Systemtakte).
  • Falls der Exponent in FAC gleich 0 ist, so wird die ROM-Routine EXP dazu missbraucht, das Resultat 1 nach FAC zu übertragen (plus 3317 Takte).
  • Falls dagegen nur die Basis in ARG gleich 0 ist, so wird als Resultat direkt FAC auf 0 gesetzt (plus 28 Takte).
  • Sind sowohl FAC als auch ARG ungleich 0, so resultiert der Rechenzeitbedarf von FPWR hauptsächlich aus den Einzellaufzeiten der zugrundeliegenden ROM-Routinen LOG, FMULT und EXP. Für jede dieser Routinen existieren Sonderfälle, in denen die Berechnung besonders schnell durchgeführt wird. Ansonsten ergaben sich für FPWR im Rahmen umfangreicher Messungen Laufzeiten zwischen 44278 und 50603 Systemtakten.

Ein Systemtakt entspricht auf dem Commodore 64 rund einer Mikrosekunde (μs). Im ungünstigsten Fall benötigt die FPWR-Routine also etwas mehr als 50 Millisekunden (ms). Für Potenzierungen mit ganzzahligen Exponenten kann es sich daher lohnen, diese Rechenoperationen auf eine Reihe von Multiplikationen zurückzuführen, da FMULT lediglich eine Laufzeit von nur rund 3 ms hat. Beispielsweise erfordert die Berechnung von a10 nur vier Multiplikationen und ist damit rund viermal schneller als der Aufruf von FPWR:

a10 = ((a2)2 × a)2

Bugs

Die ersten beiden Schritte des Algorithmus behandeln die Sonderfälle FAC = 0 und ARG = 0 nur unzureichend.

  • Im ersten Schritt (Exponent gleich 0) müsste zusätzlich auf eine Basis von 0 geprüft werden und in diesem Fall ein ?ILLEGAL QUANTITY ERROR ausgelöst werden, da der Ausdruck 00 nicht definiert ist. In allen anderen Fällen liefert FPWR das korrekte Resultat 1.
  • Im zweiten Schritt (Basis gleich 0) sollte sichergestellt werden, dass der Exponent positiv ist. Nur in diesem Fall ist das von FPWR gelieferte Resultat 0 korrekt; ansonsten stellt eine Potenzierung mit einem negativen Exponenten eine Division durch 0 dar und sollte dementsprechend einen ?DIVISION BY ZERO ERROR melden.

FPWR Error.gif

  • Im dritten Schritt nutzt FPWR die ROM-Routine INT, um zu überprüfen, ob der Exponent ganzzahlig ist, und verlässt sich anschließend darauf, dass diese Routine das niederwertigste Byte des Exponenten in den Hilfspuffer an Adresse 7/$07 kopiert. Aufgrund eines Fehlers in der ROM-Routine INT wird dieser Hilfspuffer aber für Zahlen aus den Intervallen ] -232; -231 ] und [ 231; 232 [ nicht gefüllt, so dass FPWR beim Potenzieren von negativen Werten mit einem ungeraden Exponenten aus diesen Wertebereichen fälschlicherweise ein positives Ergebnis liefert:

INT Bug.gif

Weblinks

Quellen