← ST-Computer 04 / 1990

Umwandlung von Zahlen in Strings (GFA-Basic)

Programmierpraxis

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
Thomas MĂŒller