Da freut man sich, daß TURBO/PURE C den Coprozessor des TT unterstützt. Der Compiler macht seine Sache auch wunderschön. Doch nun die schlechte Nachricht: Es gibt da einen Spielverderber, nämlich die Standardbibliothek. Und ohne sie läuft halt wenig. Da bleibt einem nichts anderes, als Ärmel hochkrempeln und sich selber ans Werk machen.
Als ich zum ersten Mal den Speedup durch die FPU testen wollte, schrieb ich ein kleines Programm in TURBO C, das ja eine Option für 68882-Unterstützung besitzt. Doch leider erschienen sehr seltsame Zahlen auf dem Bildschirm. Nach etwas Rätselraten und anschließender Analyse des erzeugten Assembler-Codes war der Schuldige entlarvt: Der vom Compiler produzierte Code verwendet FPU-Befehle und übergibt auch die Parameter in FPU-Registern. Die Funktionen in der Standardbibliothek TCFLTLIB.LIB erwarten aber ihre Parameter auf dem Stack und rechnen alles „zu Fuß“ aus. Einen Coprozessor kennen sie zwar schon, können ihn aber nur über Speicheradressen ansprechen (wie es beim 68000 nötig ist), was auf dem TT nicht mehr geht. Fazit: Die FPU wird nur für die Grundrechenarten benutzt, und man kann nicht einmal einen Sinus ausrechnen.
Die Lösung des Dilemmas war auch sofort klar: Selbst ist der Mann! Die meisten benötigten Funktionen sind Gott sei Dank direkt durch FPU-Befehle realisierbar. Dementsprechend findet man im Quelltext (Listing 1) viele Zweizeiler: FPU-Befehl und RTS. Etwas mehr Aufwand erfordern die Rundungsfunktionen ceil(), floor(), round() und trunc(). Da man nicht davon ausgehen darf, daß sich die FPU in einem bestimmten Rundungsmodus befindet, muß man den gerade gesetzten Zwischenspeichern, bevor man das Gewünschte einstellt. Den Rest erledigt dann der Befehl FINT. Für trunc() kann man sich das Ganze allerdings sparen, da es einen Befehl. FINTRZ gibt, der immer den „to zero“-Modus verwendet.
Bei frexp() tritt das Problem auf, daß laut TURBO-C-Konvention die gelieferte Mantisse im Bereich von 0.5 bis 1.0 liegen soll, der Befehl FGETMAN jedoch eine zwischen 1.0 und 2.0 erzeugt. Man muß also nachkorrigieren, sprich die Mantisse halbieren und dementsprechend den Exponenten um Eins erhöhen. Der hierbei verwendete Befehl FSCALE #x,FPy multipliziert FPy mit 2^x, was schneller ist als ein FDIV.
Es gibt jedoch auch Funktionen, die nicht direkt in die FPU eingebaut sind. Hier wären asinh(), acosh() und die Potenzierung zu nennen. Die beiden Area-Funktionen müssen über ihre Definition berechnet werden, die Formeln dazu stehen im Listing. Bei der Potenzierung nach b^e = exp(e*ln(b) hat man folgendes Problem: Mathematisch ist die Potenz zu einer negativen Basis nur definiert, wenn der Exponent ganzzahlig ist. Das Programm muß also gegebenenfalls mit dem Betrag der Basis rechnen und bei ungeradem Exponenten das Ergebnis wieder negativ machen. Getestet wird, indem man den Exponenten in ein Integer wandelt und anschließend mit dem Fließkommawert vergleicht. Will man eine Undefinierte Potenz berechnen, wird der Logarithmus einer negativen Zahl genommen, was korrekt E_DOMAIN zur Folge hat.
Die FPU hat sogar noch ein paar Funktionen mehr als die TURBO-C-Bibliothek bietet. In NEWMATH.LIB sind auch diese für C nutzbar gemacht. Hierunter fallen log2() (Zweierlogarithmus) sowie exp2() und exp10() als Umkehrfunktionen zu den Logarithmen. Auch kann der 68882 Sinus und Cosinus simultan berechnen. Die C-Funktion dazu ist sincos(), die den Sinus als Ergebnis liefert, der Cosinus wird über Zeiger zurückgegeben.
Komplett geändert wurde die Fehlerbehandlung. Da es äußerst unpraktisch ist, nach jeder Operation die Variable errno zu testen, gibt es in NEWMATH.LIB die Funktionen fclrerr() und ferr(). Die beiden Funktionen machen das Exception Accrued Byte der FPU zugänglich, indem sich diese alle Fehler merkt, bis sie ausdrücklich gelöscht werden. Man macht also vor einer Reihe von Operationen fclrerr() und kann nach deren Ausführung mit ferr() testen, ob bei irgendeiner davon ein Fehler auftrat. Die Rückgabewerte von ferr() sind in NEWMATH.H definiert und selbsterklärend. Es können auch Kombinationen der einzelnen Codes auftreten.
Die FPU kann als Ergebnisse auch Unendlich (z.B. ln 0) oder NAN („Not A Number“ = mathematisch sinnlos, z.B ln -1) zurückliefern. Solche Werte kann man mit NEWMATH.LIB durch die Funktion ftest() einfach erkennen. Es wird 1 geliefert, wenn das Argument ein NAN war, und 2 für Unendlich. Bei „normalen“ Zahlen erhält man 0. Aus dem Vorzeichen des Rückgabewerts erkennt man das Vorzeichen des Parameters (es gibt + und - Unendlich/NAN!).
Die Funktion fpumode(), die in der alten Bibliothek dazu diente, den Coprozessor ein- und auszuschalten, wird in NEWMATH.LIB verwendet, um die Rechengenauigkeit und den Rundungsmodus der FPU einzustellen. Der 68882 rechnet intern mit etwas mehr Genauigkeit, als nach außen sichtbar ist. Nach jedem Rechenschritt wird dann das Ergebnis auf die eingestellte Präzision zurechtgestutzt, wobei der eingestellte Rundungsmodus angewendet wird.
So weit, so gut: Jetzt können wir rechnen. Doch die Ergebnisse auf den Bildschirm bringen, ist wieder ein anderes Kapitel. Denn printfl) aus TCSTDLIB.LIB beherrscht sowieso keine Fließkommazah-len, und mit dem printf() aus TCFLTLIB.LIB hat man dieselben Schwierigkeiten wie mit allen anderen Routinen darin. Daher gibt es in NEWMATH.LIB auch eine Funktion zur Ausgabe von floats. Es wäre mir jedoch wesentlich zuviel Arbeit geworden, ganz printf() mit seinem riesigen Funktionsumfang neu zu schreiben. Meine Routine printfl() gibt sich dagegen ganz bescheiden aus, aber man sieht immerhin etwas Vernünftiges auf dem Bildschirm. Es wurde auf alle Formatierungs-Features verzichtet, nur Zahlen, die größer als 99999 oder kleiner als 0.01 sind, werden im Exponentialformat ausgegeben, alle anderen ohne Exponent. Schließlich sieht „10“ schöner aus als „1.0e+1“. '
Die Hauptarbeit dabei - das Umwandeln von Binär- in Dezimaldarstellung - können wir getrost der FPU überlassen. Sie kennt nämlich auch ein BCD-Format für Fließkommazahlen, das sog. Packed-Decimal-String-Format. Dabei entsprechen je 4 Bit einer Ziffer. Eine kurze Beschreibung dieses Formats finden Sie in Bild 1, für Genaueres möchte ich auf Literatur verweisen [1,2]. Die Umwandlung von BCD in ASCII erleichtert sich ebenfalls durch den 68030-Befehl UNPK („unpack“), dessen Funktionsweise man in Bild 2 dargestellt findet.
printfl() muß also eigentlich nur noch unnötige Nullen am rechten Rand streichen und entscheiden, ob Darstellung mit oder ohne Exponent angewandt werden soll. Natürlich werden auch NANs und Unendlich korrekt ausgegeben. Aufgrund der vielen Abfragen, die sich ergeben, wird die Routine leider etwas unübersichtlich und lang. Ich hoffe jedoch, daß sie mit Hilfe des Kommentars einigermaßen verständlich ist, und will hier im Text nicht ausführlich auf die Programmierung eingehen.
Ebenso wie bei printf() gibt es auch bei printfK) Varianten, die die Zahl statt auf den Bildschirm in ein File oder einen String ausgeben. Die Benutzung von flprintfl() und sprintfl() ist genau analog zu den Varianten von printfl). Realisiert werden sie durch einen Zeiger auf die Ausgaberoutine für printfl(), der je nach Einsprungpunkt auf den entsprechenden Geräte-Handler gesetzt wird. printfl() gibt genauso wie printf() die Anzahl der ausgegebenen Zeichen zurück.
Um NEWMATH.LIB zu installieren, übersetzen Sie den Quelltext von Listing 1 mit dem MAS-Assembler des TURBO-C-Pakets, benennen die entstehende .O-Datei in NEWMATH.LIB um und kopieren sie ins \LIB-Verzeichnis des Compilers. Sie wird dann anstelle von TCFLTLIB.LIB in der Projektdatei angegeben. Die für NEWMATH.LIB benötigten Prototypen und Definitionen finden sich in Listing 2. NEWMATH.H wird anstelle des alten MATH.H verwendet.
Damit kann man dann den Coprozessor des TT auch von C aus richtig nutzen, wenn auch der Overhead der Unterprogrammaufrufe etwas Zeit kostet. Praktischer wäre es natürlich, wenn der Compiler selbst z.B. den Befehl FSIN einsetzen würde, wenn die Funktion sin() im Programmtext steht. Aber das wird wohl ein frommer Wunsch bleiben. Vielleicht findet sich auch jemand, der sich die Mühe macht, ein vollständiges printfl() mit FPU-Unterstützung zu schreiben.
Literatur:
[1] Steve Williams: „68030 Assembly Language Reference“, Addison-Wesley, 1989, ISBN 0-201-08876-2
[2] Jochen Fischer: „Assembler ohne Grenzen“, ST-Computer 12/91, S. 100 ff.
; ----------------------------------------------
; NEWMATH.S
;
; Mathematische Bibliothek für TURBO/PURE C,
; die die FPU 68882 des TT unterstützt
;
; by Roman Hodek
; (c) 1992 MAXON Computer
; ----------------------------------------------
; Labels, die nach außen bekannt sein sollen;
EXPORT printf1,sprintf1,fprintf1
EXPORT fabs,ceil,floor,round,trunc,fmod
EXPORT exp,exp10,exp2,log,log10,log2
EXPORT frexp,ldexp,modf
EXPORT pow,pow10,sqrt
EXPORT sin,cos,tan,sincos,asin,acos,atan,atan2
EXPORT sinh,cosh,tanh,asinh,acosh,atanh
EXPORT ftest,fclrerr,ferr,fpumode
; für printfl benötigt:
IMPORT _StdOutF,fputc
fabs: fabs.x fp0,fp0
rts
ceil: fmove fpcr,d0 ; Rounding mode:
move.l d0,d1
and.b #%11001111,d0
or.b #%00110000,d0 ; to + infty
fmove d0,fper
fint.x fp0,fp0
fmove d1,fper
rts
floor: fmove fpcr,d0 ; Rounding mode:
move.l d0,d1
and.b #%11001111,d0
or.b #%00100000,d0 ; to - infty
fmove d0,fpcr
fint.x fp0,fp0
fmove d1,fpcr
rts
round: fmove fpcr,d0 ; Rounding mode:
move.l d0,d1
and.b #%11001111,d0
or.b #%00000000,d0 ; to nearest
fmove d0,fpcr
fint.x fp0,fp0
fmove d1,fpcr
rts
trunc: fintrz.x fp0,fp0 ; Rounding mode:
rts ; to zero
fmod: frem.x fp1,fp0
rts
exp: fetox.x fp0,fp0
rts
exp10: ftentox.x fp0,fp0
rts
exp2: ftwotox.x fp0,fp0
rts
log: flogn.x fp0,fp0
rts
log10: flog10.x fp0,fp0
rts
log2: flog2.x fp0,fp0
rts
; fgetman liefert eine Mantisse zwischen 1.0
; und 2.0; frexp() soll aber Werte zwischen
; 0.5 und 1.0 zurückgeben ==>
; man /= 2 und exp += 1
frexp: fgetexp.x fp0,fp1
fgetman.x fp0,fp0
fmove.w fp0,(a0)
addq.w #1,(a0)
fscale.w #-1,fp0
rts
ldexp: fscale.w (a0),fp0
rts
modf: fintrz.x fp0,fp1 ; FP1 <- Integerteil
fmove.x fp1,(a0) ; Nachkommateil =
fsub.x fp0,fp1 ; x - int(x)
rts
; Potenzberechnung: a^b = exp(b*ln(a))
; Ist für negative Basis nur bei ganzzahligen
; Exponenten definiert!
pow: ftst.x fp0 ; Basis == 0 ->
fbeq pow_end ; Ergebnis 0
fmove.l fp1,d0
fcmp.l d0,fp1
fbne pow_frac
; ganzzahl. Exp.:
ftst.x fp0 ; Basis >= 0 ->
fbge pow_frac ; normale Behandl.
fabs.x fp0 ; Basis < 0 :
flogn.x fp0,fp0 ; Betrag nehmen
fmul.x fp1,fp0
fetox.x fp0,fp0
btst #0,d0 ; und wenn Exponent
beq pow_end ; ungerade:
fneg.x fp0 ; Ergebnis negativ
bra pow_end
pow_frac:flogn.x fp0,fp0 ; ergibt bei negat.
fmul.x fp1,fp0 ; Exponenten
fetox.x fp0,fp0 ; E_DOMAIN
pow_end:rts
pow10: fmove.w d0,fp0
ftentox.x fp0,fp0
rts
sqrt: fsqrt.x fp0,fp0
rts
sin: fsin.x fp0,fp0
rts
cos: fcos.x fp0,fp0
rts
tan: ftan.x fp0,fp0
rts
sincos: fsincos.x fp0,fp1:fp0
fmove.x fp1,(a0)
rts
asin: fasin.x fp0,fp0
rts
acos: facos.x fp0,fp0
rts
atan: fatan.x fp0,fp0
rts
; atan2(y,x) berechnet den Arcustangens des
; Bruches y/x. Die Funktion ist vor allem zu
; Berechnungen des Arguments komplexer Zahlen
; gedacht und liefert Ergebnisse -pi .. pi
; 0 im Nenner wird korrekt gehandhabt und pi/2
; zurückgegeben.
atan2: ftst.x fp1 ; x == 0 => Erg.
fbeq atan2_pol ; +-pi/2
moveq #0,d0 ; sonst Korrektur:
fbgt atan2_fac ; x>0 : 0*pi
moveq #1,d0 ; x<0, y>=0: +1*pi
ftst.x fp0 ; " , y< 0: -1*pi
fbge atan2_fac
neg.w d0
atan2_fac:
fdiv.x fp1,fp0 ; fp0 <- atan(y/x)
fatan.x fp0
fmovecr.x #0,fp1 ; pi * Korrektur
fmul.w d0,fp1
fadd.x fp1,fp0 ; dazuaddieren
rts
atan2_pol: ; Behandlung der
fmove.x fp0,fp1 ; Polstellen x == 0
fabs.x fp0
fdiv.x fp1,fp0 ; fp0 <- sign< y )
fmovecr.x #0,fp1
fmul.x fp1,fp0 ; * pi
fscale.w #-1,fp0 ; / 2
rts
sinh: fsinh.x fp0,fp0
rts
cosh: fcosh.x fp0,fp0
rts
tanh: ftanh.x fp0,fp0
rts
asinh: fmove.x fp0,fp1 ; asinh(x) =
fmul.x fp1,fp1 ; ln(x+sqrt(x^2+1))
fadd.w #1,fp1
fsqrt.x fp1
fadd.x fp1,fp0
flogn.x fp0
rts
acosh: fmove.x fp0,fp1 ; acosh(x) =
fmul.x fp1,fp1 ; ln(x+sqrt(x^2-1))
fsub.w #1,fp1
fsqrt.x fp1
fadd.x fp1,fp0
flogn.x fp0
rts
atanh: fatanh.x fp0,fp0
rts
; -----------------------------------------------------
; neue Verwaltungsfunktionen
; ftest testet sein Argument, ob es Unendlich
; oder ein NAN ist. Rückgabe:
; +-1 => +-NAN ; +-2 => +-Unendlich
; 0 => "normale" Zahl
ftest: ftst.x fp0 ; FP0 testen
fmove.l fpsr,d0 ; Condition Codes
swap d0 ; holen
lsr.w #8,d0
btst #3,d0 ; N-Flag -> d1
sne d1
and.l #3,d0 ; X- und NAN-Flags:
tst.b d1 ; 2 = Unendl.
beq ftest_e ; 1 = NAN
neg.l d0 ; ev. negieren
ftest_e:rts
; fclrerr löscht früher bemerkte Fehler
fclrerr:fmove.l fpsr,d0 ; Accrued Excep-
and.l #$ffffff00,d0 ; tion-Byte
fmove.l d0,fpsr ; löschen
rts
; ferr testet, ob seit dem letzten fclrerr Fehler
; aufgetreten sind. Rückgabe: s. Headerdatei
ferr: fmove.l fpsr,d0 ; Accrued Exception
and.l #$ff,d0 ; Byte lesen
lsr.l #3,d0
rts
; fpumode setzt Rechengenauigkeit und Rundungs-
; modus der FPU
fpumode:fmove.l fper,d2
tst.w d0 ; wenn d0 = prec.
bmi fpm_01 ; != -1 :
lsl.w #6,d0 ; ins FPCR schreiben
and.b #%00111111,d2
or.b d0,d2
fpm_01: tst.w d1 ; wenn d1 = r.mode
bmi fpm_02 ; != -1 :
lsl.w #4,d0
and.b #%11001111,d2 ; ins FPCR
or.b d0,d2
fpm_02: fmove.l d2,fpcr ; FPCR zurückschr.
rts
; -----------------------------------------------------
; [fs]printfl zur Ausgabe von Fließkommazahlen
; f,s: selbe Bedeutung wie bei printf
; Ausgabeformat: Werte > 99999 und < 0.01
; werden mit Exponent gedruckt, sonst ohne
; Rückgabewert: Anzahl der ausgegebenen Zeichen
saving reg d4-d7/a3-a5 ; zu rettende Register
packed = -12 ; lokale Variable
buffer = packed - 16
; Einsprungpunkte je nach Ausgabegerät:
; a5 = Ausgabehandler
; a4 = Zeiger auf String/File
printf1:
link a6,#buffer
movem.l #saving,-(sp)
lea put_scr(pc),a5
bra _printfl
sprintfl:
link a6,#buffer
movem.l #saving,-(sp)
lea put_str(pc),a5
move.l a0,a4
bra _printfl
fprintfl:
link a6,#buffer
movem.l #saving,-(sp)
lea put_file(pc),a5
move.l a0,a4
_printfl:ftst.x fp0 ; Null abfangen
fbne pf_nnull
moveq #'0',d0
jsr (a5)
bra pf_end
; Zahl ins Packed-Decimal-Format
pf_nnull:fmove.p fp0,packed(a6){#17}
btst #5,packed(a6) ; NAN oder
bne pf_nan ; Unendl. ?
; mit oder ohne Exponent ?
moveq #0,d5 ; d5 = Kommapos.
sf d4 ; d4 = Exp.flag
move.b packed+2(a6),d0
and.b #$f0,d0
bne pf_mitex
move.w packed(a6),d0
move.w d0,d1
and.w #$0ff0,d1 ; |Exp.| > 9 ->
bne pf_mitex ; mit Exp. ausg.
and.w #$f,d0
btst #6,packed(a6) ; Vorzeichenbit
beq pf_01
neg.w d0 ; d0 = Exponent
pf_01:
cmp.w #-2,d0 ; -2<=Exp.<=5 =>
blt pf_mitex
cmp.w #5,d0
bgt pf_mitex
st d4 ; keinen Exp.!
move.w d0,d5 ; d5 = Komma-
; stelle
pf_mitex:moveq #0,d7 ; d7 = Zeichen-
; zähler
btst #7,packed(a6) ; wenn negativ,
beq pf_02 ; ' - ' ausgeben
moveq #'-',d0
jsr (a5)
pf_02:
tst.w d5 ; wenn Exp.flag
bpl pf_start
moveq #'0',d0 ; Exp. == -1 ->
jsr (a5) ; "0" ausg.
moveq #'.',d0 ; Exp. == -2 ->
jsr (a5) ; "0.0” ausg.
cmp.w #-1,d5
beq pf_start
moveq #'0',d0
jsr (a5)
pf_start:move.b packed+3(a6),d0 ; erste Ziffer
and.b #$0f,d0 ; ausgeben
or.b #$30,d0
jsr (a5)
lea packed+12(a6),a0 ; restliche
lea buffer+16(a6),a1 ; Ziffern
moveq #7,d0 ; entpacken
pf_03: unpk -(a0),-(a1),#$3030
dbra d0,pf_03
lea buffer+16(a6),a0 ; Nullen
lea -16(a0),a1 ; rechts weg-
pf_04: cmp.b #$30,-(a0) ; streichen
bne pf_05
clr.b (a0)
cmp.l a0,a1
bne pf_04
pf_05:
lea buffer(a6),a3 ; Nachkommateil
addq.w #1,d5 ; ausgeben
moveq #15,d6
pf_loop:cmp.w #1,d5 ; Abbruch, wenn
bgt pf_06 ; schon Komma
tst.b (a3) ; und Nullbyte
beq pf_exp
pf_06: subq.w #1,d5
bne pf_07 ; Kommaz. == 0
moveq #'.',d0 ; => '.'' ausgeb.
jsr (a5)
pf_07: move.b (a3)+,d0 ; Ziffer ausgeb.
or.b #$30,d0
jsr (a5)
dbra d6,pf_loop
; Exponent ausg.
pf_exp: tst.b d4 ; nur wenn d4
bne pf_end ; != 0
move.w packed(a6),d0 ; Exp. ext ra-
and.w #$fff,d0 ; hieren
move.w packed+2(a6),d1
and.w #$f000,d1 ; Erweiterungs-
or.w d1,d0 ; stelle!
lea buffer(a6),a3
unpk d0,d1,#$3030 ; Exp. entpacken
move.w d1,2(a3)
lsr.w #8,d0
unpk d0,d1,#$3030
move.w d1,(a3)
moveq #'e',d0 ; 'e'-Zeichen
jsr (a5)
moveq #'+',d0 ; Vorzeichen
btst #6,packed(a6) ; ausgeben
beq pf_08
moveq #'-',d0
pf_08: jsr (a5)
moveq #3, d6
pf_09: subq.w #1,d6
cmp.b #$30,(a3)+ ; führende 0en
beq pf_09 ; überspringen
subq.l #1,a3
addq.w #1,d6
pf_10: move.b (a3)+,d0 ; und Exponent
jsr (a5) ; ausgeben
dbra d6,pf_10
bra pf_end
pf_nan: tst.l packed+4(a6) ; Unterscheidung
bne pf_na ; NAN oder Unen.
tst.l packed+8(a6)
bne pf_na
move.b packed+3(a6),d0
and.b #15,d0
bne pf_na
moveq #'+',d0 ; +- Unendlich
btst #7,packed(a6)
beq pf_11
moveq #'-',d0
pf_11: jsr (a5)
move.b #$df,d0 ; Unendlich-
jsr (a5) ; Zeichen
bra pf_end ; ausgeben
pf_na: moveq #'N',d0 ; "NAN" ausgeben
jsr (a5)
moveq #'A',d0
jsr (a5)
moveq #'N',d0
jsr (a5)
pf_end: cmp.l #put_str,a5 ; bei Ausg. in.
bne pf_12 ; String \0 an-
clr.b (a4) ; hängen
pf_12: move.w d7,d0 ; Rückgabewert
movem.l (sp)+,#saving
unlk a6
rts
; Ausgabehandler für Strings, Files und STDOUT
put_str:move.b d0,(a4)+
addq.w #1,d7
rts
put_scr:lea _StdOutF,a0
bsr fputc
addq.w #1,d7
rts
put_file:move.l a4,a0
bsr fputc
addq.w #1,d7
rts
Listing 1: NEWMATH.S: Quelltext der Bibliothek NEWMATH.LIB
/* NEWMATH.H
(c) 1992 MAXON Computer
Headerdatei für die FPU-Bibliothek
NEWMATH.LIB
Änderungen gegenüber dem alten MATH.H:
- ftoa ersetzt durch [fs]printfl
- andere Fehlerbehandlung : fclrerr, ferr
- neue Fkt.: sincos, log2, exp10, exp2
- ftest zum Testen von Unendlich und NAN's
- fpumode jetzt zum Setzen von Rechengenauigkeit und Rundungsmodus
*/
#if !defined( __MATH )
#define __MATH
#define HUGE_VAL 1.189731495357231765E+4932
#define M_E 2.71828182845904523536
#define M_LOG2E 1.44269504088896340736
#define M_LOG10E 0.434294481903251827651
#define M_LN2 0.693147180559945309417
#define M_LN10 2.30258509299404568402
#define M_PI 3.14159265358979323846
#define M_PI_2 1.57079632679489661923
#define M_PI_4 0.785398163397448309116
#define M_1_PI 0.318309886183790671538
#define M_2_PI 0.636619772367581343076
#define M_1_SQRTPI 0.564189583547756286948
#define M_2_SQRTPI 1.12837916709551257390
#define M_SQRT2 1.41421356237309504880
#define M_SQRT_2 0.707106781186547524401
int printfl( double x );
int fprintfl( FILE *file, double x );
int sprintfl( char *str, double x );
double fabs( double x );
double ceil( double x );
double floor( double x );
double round( double x );
double trunc( double x );
double fmod( double x, double y );
double exp( double x );
double exp10( double x );
double exp2( double x );
double log( double x );
double log10( double x );
double log2( double x );
double frexp( double x, int *nptr );
double ldexp( double x, int n );
double modf( double x, double *nptr );
double pow( double x, double y );
double pow10( int i );
double sqrt( double x );
double cos( double x );
double sin( double x );
double tan( double x );
double sincos( double x, double *c );
double acos( double x );
double asin( double x );
double atan( double x );
double atan2( double x, double y );
double cosh( double x );
double sinh( double x );
double tanh( double x );
double acosh( double x );
double asinh( double x );
double atanh( double x );
double atof( const char *digStr );
int ftest( double x );
void fclrerr( void );
int ferr( void );
/* Rückgabewerte von ferr() */
#define E_INEXACT 1
#define E_SING 2
#define E_OVERFLOW 4
#define E_UNDERFLOW 8
#define E_DOMAIN 16
void fpumode( int prec, int round );
/* Präzisionen für fpumode() */
#define P_EXTENDED 0
#define P_SINGLE 1
#define P_DOUBLE 2
/* Rundungsmodi für fpumode() */
#define RM_NEAREST 0
#define RM_ZERO 1
#define RM_MIFTY 2
#define RM_PIFTY 3
#endif
Listing 2: NEWMATH.H: Header-Datei zu NEWMATH.LIB