Mancher, der mit dem ATARI ST zu tun hat, wird vielleicht selten vor dem Problem stehen, eine Zahl als String in einem wissenschaftlich-technischen Format darstellen zu mĂŒssen. Mir als Student in einem wissenschaftlich-technischen Studiengang stellte es sich regelmĂ€ssig, wenn es an die Auswertung irgendwelcher LaborĂŒbungen ging. Diese stupide Arbeit durfte (und darf) âSarotti Computerâ erledigen - per Programm.
Mit den Programmen steht es genauso wie mit dem HÀusle: StÀndig ist irgendwas zu Àndern, zu reparieren, besser zu machen. Nicht nur eigene Programme werden stÀndig verbessert, auch die Programmiersprachen werden immer besser, komfortabler und leistungsfÀhiger. So auch das neue GFA BASIC 3.xx. Dort wurde unter anderem die Funktion STR$() um ganze drei optionale (!!) Parameter erweitert.
FĂŒr die normale Ausgabe 123.125 ist â 123.13â völlig ausreichend. (Schon dafĂŒr ist man sehr dankbar, weil Ă€uĂerst selten.) Leider fehlten aber die Exponential-Schreibweisen, wie sie immer hĂ€ufiger anzutreffen sind (fĂŒr das Beispiel 123.125):
1.23125E+002 (technische Exponentialschreibweise),
0.123125E+003 (wissenschaftliche Schreibweise, PASCAL lĂ€Ăt grĂŒĂen).
Mein Ziel war, eine Routine zu entwickeln, die es erlaubt, daĂ sich in den Exponential-Schreibweisen das Zahlenformat m.dd...E±eee auĂer dem Vorzeichen der Mantisse m.dd... nicht Ă€ndern soll, was bei normaler Schreibweise leider der Fall ist.
Deshalb wurde ein konstant dreistelliger Exponent gewÀhlt, der im Bereich 000 bis ±308 liegen kann.
Grundfunktion in dieser Routine ist, wie sollte es auch anders sein, die Funktion STR$(x,y,z)1. Die minimale LĂ€nge des Zahl-Strings setzt sich zusammen aus der Anzahl der Vorkomma-Stellen (VK) plus der Anzahl der Nachkommastellen (NK) plus einer Stelle fĂŒr den Dezimalpunkt plus einer fĂŒr das Vorzeichen (normale Schreibweise):
len = VK+NK+2 (1).
Bei den Exponentialschreib-weisen ist die Bestimmung der StringlĂ€nge etwas einfacher. Hier haben wir ein Vorzeichen, eine Vorkommastelle, einen Dezimalpunkt, die Anzahl der Nachkommastellen, ein âEâ, das Exponentenvorzeichen und den dreistelligen Exponenten. Es gilt:
len = 3+NK+5 = NK+8 (2).
Oftmals ist die Angabe eines Formates mit einer ĂŒblen Kopfrechnerei verbunden, weil alle mir bekannten Programmiersprachen die gesamte Anzahl an Stellen wissen wollen und die Anzahl der Nachkommastellen, wobei der Dezimalpunkt als eine Stelle gezĂ€hlt wird. Und weil Programmierer eben recht faul sind, was das Kopfrechnen angeht, kann bei zwei anzugebenden Parametern auch gleich die Anzahl der Vorkomma- und Nachkommastellen ĂŒbergeben werden. Dann geben wir auch gleich noch an, welches Format wir haben wollen.
Da die Routine als Hilfe zur Dateneingabe innerhalb eines Dialogs fungieren soll, geben wir auch gleich an, wie lang der String am Ende sein sollte. Die Routine ist so ausgelegt, daĂ eine zu kleine String-LĂ€nge schlichtweg ignoriert wird. Eine zu groĂe wird mit Leerzeichen am Anfang aufgefĂŒllt. Doch wie gehen wir nun vor?
ZunĂ€chst wird die ĂŒbergebene Zahl âdataâ in die lokale Variable âdâ kopiert, schlieĂlich sollen irgendwelche Schwierigkeiten dadurch, daĂ âdataâ kein VAR-Parameter ist, ausgeschlossen werden, auĂerdem zeugt es meiner Meinung nach von einem âsauberenâ Programmierstil.
Nehmen wir den einfachsten Fall, das Ausgabeformat Null. Dabei wird mit der ĂŒbergebenen Zahl nichts weiter angestellt, Sondern nach der Gleichung (1) ein Zahl-String aufgebaut. Bei positiven Zahlen steht an erster Stelle ein Leer-und bei negativen ein Minuszeichen. Wem das nicht gefĂ€llt, der kann auch das Pluszeichen ausgeben mit:
MID$(d$,1,1)=â+â,
wobei die Wahl der nötigen Abfrage dem geneigten Leser selbst ĂŒberlassen sei. Damit ist dieser Fall zunĂ€chst erledigt. Nun wenden wir uns der technischen Exponential-Schreibweise zu. Sie findet in Taschenrechnern ihre Anwendung und ist meiner Meinung nach fĂŒr den Hausgebrauch die beste. Ein Manko hat sie aber doch, wie wir noch sehen werden.
ZunĂ€chst stellen wir fest, wie groĂ die Zahl ĂŒberhaupt ist. Das geschieht mit der dekadischen Logarithmus-Funktion. DaĂ hierbei der Absolutwert benutzt wird, dĂŒrfte verstĂ€ndlich sein. Wenn |d| kleiner eins ist, erhalten wir in lg& eine Zahl, deren Betrag um eins zu groĂ ist, deshalb muĂ lg& um eins erniedrigt werden. Dann wird die Mantisse berechnet, indem man âdâ durch 10lg& dividiert. Da der STR$()-Befehl seinerseits eine Rundung vornimmt, kann es passieren, daĂ statt 9.9995001 plötzlich â 10.000' ausgegeben wird. Das verhindert man, indem man eine entsprechende Abfrage einbaut, bei der dieser Fall geprĂŒft wird. Sehen wir uns diese Abfrage etwas genauer an:
IF INT(d*10^dez|+0.5 +10^(-dez|+3)))/10^dez|
Der Term 10^dez| bzw. INT(d*10^dez|)/10^dez| schneidet bis auf 3 Nachkommastellen (dez|=3) alle Dezimalen ab. Addiert man in der INT()-Anweisung dann noch die Zahl 0.5, wird die letzte Stelle gerundet. Bei Verwendung des STR$()-Befehls reichte diese einfache Rundung nicht mehr aus, irgendwie verarbeiten beide Anweisungen die gleiche Zahl anders, so daĂ trotz dieser Abfrage die Ausgabe der Routine â10.000E+000â erfolgte, statt der korrekten Ausgabe â1.000E+001'. Deshalb fĂŒgen wir noch eine unauffĂ€llige kleine Zahl zu den 0.5 hinzu, damit die Ausgabe korrekt erfolgt. Câest la vie...
Danach wird der Mantissen-String aufgebaut, dann derEx-ponenten-String, bei dem auch gleich auf LĂ€nge und Vorzeichen geachtet wird. SchlieĂlich fĂŒgt man alles in der Variablen d$ zusammen.
Bei der Ausgabe im wissenschaftlichen Exponentialformat ist die Vorgehensweise entsprechend. Nur daĂ bei positivem Exponenten die in lg& erhaltene Zahl um eins zu klein ist und deshalb lg& inkrementiert werden muĂ. Auch die Abfrage fĂŒr den Mantissen-String sieht etwas anders aus, ist deren Betrag doch immer kleiner eins. Damit sind wir fast am Ende. Vor RĂŒcklieferung des Strings fĂŒllt man ihn gegebenenfalls mit Leerzeichen auf. Zu lange Strings werden nicht gekĂŒrzt, da das die Routine unnötig verlangsamt hĂ€tte.
Eingangs erwĂ€hnte ich einen Nachteil der technischen Exponentialschreibweise. Er liegt im Zahlenbereich. WĂ€hrend die Routine im wissenschaftlichen Exponentialformat Zahlen mit BetrĂ€gen kleiner 10-307 darstellen kann, ist bei der technischen Exponentialschreibweise bei diesem Wert das Ende der Fahnenstange erreicht. Das liegt daran, daĂ zur Darstellung von 9.5E-308 die Zahl durch 1.0E -308 dividiert werden muĂ, was im GFA-BASIC gleich Null (Unterlauf) ist. Deshalb liefert die Routine fĂŒr Zahlen kleiner 1.0E-307 immer Null.
Wird die wissenschaftliche Exponentialschreibweise gewĂ€hlt, kann man Zahlen kleiner 1.0E-307 darstellen, da die Mantisse bereits durch 10 dividiert worden, der Exponent also um eins gröĂer ist. 1.0E -307 ergibt also 0.1E-306. Dagegen gibt 1.0 den Ausdruck 0.1E+001, wodurch wir sofort sehen, daĂ 1.0E+308 als 0.1E+309 ausgegeben wird, die Mantisse also durch 1.0E+309 dividiert wird, was einen Ăberlauf bewirkt, der bei der technischen Schreibweise nicht auftaucht. Der Spielraum zwischen Ober- und Untergrenze der ausgebbaren Zahlen betrĂ€gt also bei beiden Formaten 615 Zehnerpotenzen. Nur die Bereiche sind unterschiedlich. FĂŒr diejenigen, die es beim Listing vielleicht ĂŒbersehen haben sollten: Diese Routine ist als FUNCTION ausgebildet, so daĂ der zurĂŒckgelieferte String sofort zugewiesen werden kann.
' ******************************
' * *
' * Eine neue str$()-Routine *
' * *
' * *
' * Written by T.W. MĂŒller *
' * Guerickestr. 26 *
' * *
' * 1000 Berlin 10 *
' * *
' ******************************
' * Copyright by MAXON Computer*
' * (C) 1989 *
' * *
' ******************************
'
' Ab i|=7 ĂŒberschreitet der Zahl-
' string die LĂ€nge von 14 Zeichen.
' Trotzdem wird er nicht gekĂŒrzt
' ausgegeben
'
PRINT @str$(123.125,10,0,5,4)
'
FOR i|=1 TO 8
PRINT i|,@str$(999.99500005,14,1,0,i|)
PRINT i|,@str$(999.99500005,14,2,0,i|)
IF i|<8
PRINT
ENDIF
NEXT i|
'
FUNCTION str$(data,len|,art|,ziff|,dez|)
LOCAL d$,lg&,d,ex$
d=data
IF d=0
d=1.0E-300
ENDIF
SELECT art|
CASE 0
d$=STR$(d,ziff|+dez|+2,dez|)
CASE 1
lg&=LOG10(ABS(d))
IF ABS(d)<1
DEC lg&
ENDIF
d=d/(10^lg&)
IF INT(d*10^dez|+0.5+10^(-(dez|+3)))/10^dez|=10
INC lg&
d=d/10
ENDIF
ex$=STR$(ABS(lg&))
ex$=STRING$(3-LEN(ex$),"0")+ex$
IF lg&<0
ex$="-"+ex$
ELSE
ex$="+"+ex$
ENDIF
d$=STR$(d,dez|+3,dez|)+"E"+ex$
CASE 2
lg&=WORD(LOG10(ABS(d)))
IF SGN(lg&)=1
INC lg&
ENDIF
d=d/10^lg&
IF INT(d*10^dez|+0.5+10^(-(dez|+3)))/10^dez|=1
INC lg&
d=d/10
ENDIF
ex$=STR$(ABS(lg&))
ex$=STRING$(3-LEN(ex$),"0â)+ex$
IF lg&<0
ex$="-"+ex$
ELSE
ex$="+"+ex$
ENDIF
d$=STR$(d,dez|+3,dez|)+"E"+ex$
ENDSELECT
IF LEN(d$)<len|
d$=SPACE$(len|-LEN(d$))+d$
ENDIF
RETURN d$
ENDFUNC