FOUT
Anmerkung: Dieser Artikel beschreibt die numerische FOUT-Routine im BASIC-ROM.
Name: | FOUT | ||||||
Beschreibung: | Zahl im Fließkommaregister FAC zu nullterminiertem String wandeln | ||||||
Einsprungpunkt: | $BDDD / 48605 | ||||||
Übergebene Argumente: | |||||||
Sonstige: | FAC = umzuwandelnde Fließkommazahl | ||||||
Rückgabe-Werte: | |||||||
Akkumulator: | 0 = Low-Byte der Anfangsadresse des Strings | ||||||
Y-Register: | 1 = High-Byte der Anfangsadresse des Strings | ||||||
Sonstige: | Puffer ab Adresse 256/$0100 = Wert von FAC als ASCII-String |
FOUT — manchmal auch als FLPSTR[1] bezeichnet — wandelt den Inhalt des Fließkommaregisters FAC in einen nullterminierten ASCII-String ab Adresse 256/$0100 um. Nach dem Aufruf ist der Inhalt von FAC undefiniert; ein Zeiger auf den resultierenden String wird in dem Akkumulator (Low-Byte) und im Y-Register (High-Byte) zurückgeliefert.
FOUT verändert neben dem Fließkommaregister FAC und dem Stringpuffer ab Adresse 256/$0100 auch die Hilfspuffer an den Adressen 71/$47, 93/$5D, 94/$5E und 113/$71.
Algorithmus[Bearbeiten | Quelltext bearbeiten]
- Zunächst überprüft FOUT das Vorzeichen von FAC und speichert in Abhängigkeit davon als erstes Zeichen des Strings ein Leerzeichen (wenn FAC positiv) oder ein Minuszeichen (wenn FAC negativ). Anschließend wird das Vorzeichenflag von FAC auf "positiv" gesetzt; alle nachfolgenden Rechenschritte basieren also auf dem Betrag von FAC.
- Falls in FAC der Wert 0 gespeichert ist, so wird dies als Sonderfall erkannt und direkt nur die Ziffer "0" ausgegeben.
- Ansonsten bringt FOUT der Wert von FAC in das Intervall [ 99999999.9; 999999999 ]. Hierfür wird FAC so lange mit 10 multipliziert (mittels MUL10) oder durch 10 dividiert (mittels DIV10), bis sein Wert in diesem Bereich liegt. Um Rechenzeit zu sparen, prüft FOUT aber zunächst, ob der Wert von FAC kleiner als 1 ist — in diesem Fall ist mindestens eine Multiplikation mit 109 = 1000000000 erforderlich, und dies lässt sich mit Hilfe von FMULT schneller bewerkstelligen als mit neun aufeinanderfolgenden Aufrufen von MUL10. Die Anzahl der Multiplikationen bzw. Divisionen wird als positiver bzw. negativer Wert an Adresse 93/$5D festgehalten.
- Die Zahl der Multiplikationen bzw. Divisionen wird in eine Positionsangabe für den Dezimalpunkt (gespeichert an Adresse 93/$5D) und einen Exponenten (an Adresse 94/$5E) umgerechnet. War der Betrag von FAC ursprünglich kleiner als 0.01 bzw. größer oder gleich 1E+09, so ergibt sich ein Exponent im Bereich -38...-3 (als Zweierkomplement) bzw. im Bereich 9...38 und ein Dezimalpunkt nach der ersten Ziffer (Position 1). Ansonsten bekommt der Exponent den Wert 0 und der Dezimalpunkt hat eine Position zwischen -1 (beispielsweise bei der Zahl 0.01) und 9 (bei der Zahl 100000000).
- FAC wird nun in eine Ganzzahl im Intervall [ 100000000; 999999999 ] umgewandelt. Zu dieser Zahl werden dann nacheinander die Werte -100000000, 10000000, -1000000, 100000, -10000, 1000, -100, 10 und -1 jeweils so lange addiert, bis sich durch die Addition das Vorzeichen ändert. Aus der Anzahl der dafür notwendigen Additionen ergibt sich jeweils die nächste auszugebende Ziffer. Erreicht der Positionszähler für den Dezimalpunkt beim Herunterzählen den Wert 0, so wird zusätzlich das Zeichen "." ausgegeben
- Damit der so gebildete String nicht mit der Ziffer "0" oder mit einem Dezimalpunkt endet, wird der String nun vom Ende her auf diese Zeichen untersucht und diese gegebenenfalls gelöscht. Nach dem Löschen des Dezimalpunkts oder dem Auffinden einer von "0" verschiedenen Ziffer wird diese Bereinigung abgebrochen.
- Falls der Exponentenzähler an Adresse 94/$5E den Wert 0 hat, so ist keine wissenschaftliche Notation erforderlich und die Umwandlung von FAC in einen String ist zu Ende. Ansonsten gibt FOUT nacheinander noch das Zeichen "E", das Vorzeichen des Exponenten sowie — immer zweistellig — den Exponenten selbst aus.
Laufzeitverhalten[Bearbeiten | Quelltext bearbeiten]
Wenn im Fließkommaakku FAC der Wert 0 übergeben wird, so erkennt FOUT diesem Sonderfall und benötigt nur 61 Systemtakte. Ansonsten ist die Rechenzeit für die Wandlung hauptsächlich bestimmt durch die Anzahl der Multiplikationen mit 10 oder Divisionen durch 10, die Umwandlung in eine Ganzzahl und das wiederholte Addieren von positiven und negativen Zehnerpotenzen. Bei ausführlichen Tests dauerte die Wandlung zwischen 2136 Takten (für FAC = 190909090) und 88762 Takten (für FAC = 1.094909+38).
Das linke der beiden folgenden Bilder zeigt die Zeit für die Umwandlung in einen String, wenn FAC nacheinander mit allen Kombinationen aus den Ziffern 1, ..., 9 und den Exponenten E-37, ..., E+37 geladen wird. Auf der Abszissenachse aufgetragen ist die Summe Exponent + (Ziffer-1) / 9, so dass die Messwerte äquidistant eingezeichnet werden. Deutlich zu sehen ist, dass für sehr kleine Zahlen (zahlreiche Aufrufe der ROM-Routine MUL10) und sehr große Zahlen (zahlreiche Aufrufe von DIV10) die Laufzeit jeweils ansteigt. Weil DIV10 deutlich langsamer ist als MUL10, ergeben sich die höchsten Laufzeiten bei der Umwandlung von sehr großen Fließkommazahlen.
Das rechte Schaubild veranschaulicht die Laufzeit bei der Umwandlung der Zahlen 1, ..., 1000000 in Strings. Als Abszisse dient hier für jede Zahl x der Wert log10(x)+1, entsprechend in etwa der Zahl der Ziffern von x. Neben dem Einfluss des Exponenten ist bei den hier aufgetragenen Laufzeiten auch das Zusammenwirken der einzelnen Ziffern erkennbar. In diesem Wertebereich liegt die kürzeste Laufzeit bei 3946 Systemtakten (für die Zahl 190909), die längste bei 7942 Takten (für die Zahl 9). Entgegen naheliegenden Erwartungen sind die einstelligen Zahlen hier also diejenigen, deren Umwandlung in einen String am längsten dauert.
Ein Systemtakt entspricht auf dem Commodore 64 rund einer Mikrosekunde (μs). Im ungünstigsten Fall benötigt die FOUT-Routine also für die Umwandlung rund 89 Millisekunden (ms).
Bugs[Bearbeiten | Quelltext bearbeiten]
Im dritten Schritt des Algorithmus wird der Wert von FAC in das Intervall [ 99999999.90625; 999999999.25 ] gebracht. Für eine korrekte Umwandlung muss der Wert allerdings mindestens 99999999.95 betragen, sonst können bei der Umwandlung in einen String Rundungsfehler auftreten:
?99999999.9,99999999.91 99999999.9 100000000 |