20.3 Mathematische Funktionen – <math.h>, <tgmath.h> und <complex.h> 

Die Standard-Bibliothek beinhaltet mittlerweile eine gewaltige Sammlung von mathematischen Funktionen. Die meisten dieser Funktionen sind in der Headerdatei <math.h> deklariert. Die meisten dieser Funktionen sind für Gleitpunktzahlen und zum Teil auch für komplexe Gleitpunkttypen (aus der Headerdatei <complex.h>) geeignet. Zwar bietet die Standard-Bibliothek auch einige Funktionen für ganzzahlige Typen, diese sind aber alle vorwiegend in der Headerdatei <stdlib.h> bzw. für den Typ intmax_t in <inttypes.h> deklariert. Des Weiteren sind in der Headerdatei <tgmath.h> typengenerische Makros definiert, mit denen es möglich ist, mathematische Funktionen mit einem einheitlichen Namen, unabhängig vom Typ der Argumente, aufzurufen.
Um mit den anschließenden Tabellen nicht den Rahmen des Buches zu sprengen, werden zur besseren Übersicht nur die Funktionen für die Gleitpunkttypen double und double _Complex aufgelistet. Zu jeder dieser Funktionen gibt es auch eine Version mit float bzw. float _Complex und eine Version für long double bzw. long double _Complex. Die Versionen von float bzw. float _Complex haben das Suffix f am Ende des Funktionsnamens, und die Versionen für long double bzw. long double _Complex haben das Suffix l am Ende. Sofern Sie allerdings die Headerdatei <tgmath.h> verwenden, können Sie dies außer Acht lassen. Mehr dazu erfahren Sie in Abschnitt 20.3.4.
Wenn ich beispielsweise die Funktion zum Ziehen der Quadratwurzel für reelle Zahlen wie folgt aufliste:
double sqrt(double zahl);
dann existieren von dieser Funktion noch die Versionen:
float sqrtf(float zahl); long double sqrtl(long double zahl);
Gleiches gilt auch für die aufgelistete komplexe Gleitpunkttyp-Version, nur dass diese Funktionen noch zusätzlich mit dem Präfix c beginnen:
double complex csqrt(double complex z);
Auch von dieser Version gibt es noch zwei weitere Versionen:
float complex csqrtf(float complex z); long double complex csqrtl(long double complex z);
Hinweis für Linux-User |
Damit ein Programm die <math.h>-Bibliothek verwenden kann, muss diese erst mit dem Compiler-Flag -lm hinzugelinkt werden. Beispiel: gcc -o programm programm.c -lm |
20.3.1 Funktionen für reelle und komplexe Gleitpunkttypen 

In Tabelle 20.2 finden Sie Funktionen aus der Headerdatei <complex.h> und <math.h>, die Sie für reelle und komplexe Gleitpunkttypen verwenden können.
Hinweis |
<complex.h> wurde erst mit dem C99-Standard eingeführt, was natürlich wieder heißt, dass der Compiler dies wiederum nur unterstützt, wenn er C99-konform ist. |
Funktion in <math.h> | Funktion in <complex.h> | Beschreibung |
double cosh( double z ); 1) double sinh( double z ); 1) double tanh( double z); 1) |
double complex ccosh( double complex z ); double complex csinh( double complex z ); double complex ctanh( double complex z); |
Hyperbolische Funktionen |
double acos( double z ); |
double complex cacos( double complex z ); |
Arcuscosinus |
double asin( double z ); |
double complex casin( double complex z ); |
Arcussinus |
double atan( double z ); |
double complex catan( double complex z ); |
Arcustangens |
double cos( double z ); |
double complex ccos( double complex z ); |
Cosinus |
double sin( double z ); |
double complex csin( double complex z ); |
Sinus |
double tan( double z); |
double complex ctan( double complex z); |
Tangens |
double cosh( double z); |
double complex ccosh( double complex z); |
Cosinus hyperbolicus |
double sinh( double z); |
double complex casinh( double complex z); |
Sinus hypberbolicus |
double tanh( double z); |
double complex ctanh( double complex z); |
Tangens hypberbolicus |
double exp( double z); |
double complex cexp( double complex z); |
Exponentialfunktion berechnen |
double log( double z); |
double complex clog( double complex z); |
Logarithmus von z zur Basis e = 2.71828 ... |
double sqrt( double z); |
double complex csqrt( double complex z); |
Quadratwurzel |
double fabs( double z); |
double cabs( double complex z); |
Absolutwert |
double pow( double z1, double z2) ; |
double complex cpow( double complex x, double complex y); |
Potenz z1z2 |
1) Diese Funktionen wurden erst mit dem C99-Standard eingeführt. |
20.3.2 Funktionen nur für reelle Gleitpunkttypen 

Die Funktionen in Tabelle 20.3 stehen nur für reelle Gleitpunkttypen zur Verfügung und sind in der Headerdatei <math.h> definiert.
Funktion | Beschreibung |
double atan2( double x1, double x2); |
Arcustangens von x1 und x2 |
double exp(double x); double exp2(double x); 1) double frexp( double x, int x2); double ldexp( double x, int exp); double scalbn( double x, int n); 1) double scalbln( double x, long int n); 1) |
Exponentialfunktionen |
double log(double x); double log10(double x) double log1p(double x); 1) double log2(double x); 1) double logb(double x); 1) int ilogb(double x); 1) |
Logarithmusfunktionen |
double fabs(double x); |
Absolutwert |
double nextafter( double x, double y); 1) double nexttoward( double x, long double y); 1) |
Nächste darstellbare Zahl |
double fma( double x, double y, double z); 1) |
Multiplikation und Addition |
double fdim( double x, double y); 1) |
Positive Differenz |
double trunc(double x); 1) long long int llround( double x); 1) long int lround(double x); 1) double round(double x); 1) long long int llrint( double x); 1) long int lrint(double x); 1) double rint(double x); 1) double nearbyint(double x); 1) |
Rundungsfunktionen |
double floor(double x); double ceil(double x); |
Nächste Ganzzahl runden |
double modf( double1 x1, double2 *x2); |
Zerlegt den Wert von x1 in einen gebrochenen und einen ganzzahligen Wert. Der ganzzahlige Wert (Vorkommateil) befindet sich dann in der Adresse von x2. |
int fmod( double x1, double x2); double remainder( double x, double y); 1) double remquo( double x, double y, int *quo); 1) |
Rest einer Division |
double hypot( double x, double y); double cbrt(double x); 1) |
Wurzelfunktionen |
double erf(double x); 1) double erfc(double x); 1) |
Fehlerfunktionen zur Normalverteilung |
double fmin( double x, double y); double fmax( double x, double y); |
Minimum und Maximum |
double lgamma(double x); 1) double tgamma(double x); 1) |
Gammafunktionen |
double copysign( double x, double y); 1) |
Vorzeichen zuordnen |
double nan( const char *tagp); 1) |
Ein NaN erzeugen |
1) Diese Funktionen wurden erst mit dem C99-Standard eingeführt. |
20.3.3 Funktionen nur für komplexe Gleitpunkttypen 

Zum Schluss fehlt nur noch die Tabelle mit den mathematischen Funktionen aus der Headerdatei <complex.h>, die nur für komplexe Gleitpunkttypen vorhanden sind.
Funktion | Beschreibung |
double cimag(double complex z); double creal(double complex z); |
der Imaginär- bzw. komplexe Teil der komplexen Zahl |
double complex cproj( double complex z); |
komplexe Projektion auf die Riemann‘sche Sphäre |
double complex conj( double complex z); |
konjugierte komplexe Zahl |
double carg(double complex z); |
Winkel in den Polarkoordinaten (komplexes Argument) |
Hierzu folgt ein einfaches Beispiel, das diese Funktionen in der Praxis demonstrieren soll. Da immer noch diverse Compiler den C99-Standard implementiert haben und somit komplexe Zahlen nicht verwenden können, habe ich hier das Makro zur Überprüfung auf den neueren C99-Standard verwendet.
/* mathematik1.c */ #include <stdio.h> #include <stdlib.h> /* bei Linux das Compiler-Flag -lm mit angeben */ #include <math.h> #if __STDC_VERSION__ >= 19901L #include <complex.h> #endif int main(void) { double i=5.5, pi; #if __STDC_VERSION__ >= 19901L double complex c; #endif // Berechnungen mit reellen Zahlen printf("Quadratwurzel von %f = %f\n",i,sqrt(i)); printf("Der Sinus von %f = %f\n",i,sin(i)); printf("Der Tangens von %f = %f\n",i,tan(i)); printf("Der Cosinus von %f = %f\n",i,cos(i)); #if __STDC_VERSION__ >= 19901L // Berechnung mit komplexen Zahlen pi = 4 * atan(1.0); c = cexp(I * pi); printf("%f + %f * i\n", creal(c), cimag(c)); #endif return EXIT_SUCCESS; }
Sollte der double-Wert nicht mehr richtig darstellbar sein, geben all diese Funktionen die Konstante HUGE_VAL zurück, die ebenfalls in der Headerdatei <math.h> deklariert ist. Für die float- bzw. long double-Funktionen sind für die Makros HUGE_VALF und HUGE_VALL definiert.
20.3.4 Typengenerische Makros – <tgmath.h> 

<tgmath.h> wurde mit dem C99-Standard eingeführt. In <tgmath.h> sind die Headerdateien <math.h> und <complex.h> inkludiert und definieren typengenerische Makros. Der Vorteil dieser Makros liegt darin, dass Sie unabhängig vom Typ des Arguments die mathematischen Funktionen mit demselben Namen aufrufen können. Das bedeutet, Sie können außer Acht lassen, welche mathematische Funktionen Sie für den Typ float, double, long double, float complex, double complex und long double complex aufrufen.
Wollen Sie beispielsweise eine Funktion zum Ziehen der Quadratwurzel verwenden, so mussten Sie, abhängig vom Datentyp, zwischen sechs verschiedenen Varianten mit sqrtf(), sqrt(), sqrtl(), csqrtf(), csqrt() und csqrtl() unterscheiden. Mit den typengenerischen Makros in <tgmath.h> brauchen Sie sich darum keine Gedanken mehr machen. Hier müssen Sie lediglich die Funktionen der double- bzw. double complex-Variante kennen, und ein Aufruf von sqrt() führt automatisch die entsprechende Erweiterung aus. Rufen Sie beispielsweise sqrt() mit einem float complex-Argument aus, wird automatisch die Erweiterung csqrtf() ausgeführt.
Hierzu folgt ein Beispiel, das diese typengenerischen Makros demonstrieren soll. In diesem Beispiel wird für alle reellen und komplexen Gleitpunkttypen die Funktion sqrt() zum Ziehen der Quadratwurzel aufgerufen, was ohne die typengenerischen Makros nicht möglich gewesen wäre (und bei Compilern, die den C99-Standard nicht vollständig unterstützen, auch nicht möglich ist). Hier sehen Sie das Listing:
/* mathematik2.c */ #include <stdio.h> #include <stdlib.h> /* bei Linux den Compiler-Flag -lm mit angeben */ #include <tgmath.h> int main(void) { float f = 1.1; double d=2.2; long double ld = 3.3; float complex fc = 1.0 + 2.0*I, fcval; double complex dc = 4.0 + 2.0*I, dcval; long double complex ldc = 8.0 + 9.0*I, ldcval; // Berechnungen mit reellen Zahlen printf("Quadratwurzel von %f = %f\n",f,sqrt(f)); printf("Quadratwurzel von %f = %f\n",d,sqrt(d)); printf("Quadradwurzel von %Lf = %Lf\n",ld,sqrt(ld)); fcval = sqrt(fc); printf("Quadratwurzel von %f + %fi = Lf\n", creal(fcval), cimag(fcval)); dcval = sqrt(dc); printf("Quadratwurzel von %f + %fi\n", creal(dcval), cimag(dcval)); ldcval = sqrt(ldc); printf("Quadratwurzel von %Lf + %Lfi\n", creal(ldcval), cimag(ldcval)); return EXIT_SUCCESS; }
20.3.5 Gleitpunktwerte klassifizieren 

Ein weiteres interessantes Feature sind Makros zur Bestimmung der Kategorie von Gleitpunktzahlen. Seit dem C99-Standard werden die Gleitpunktzahlen in folgende fünf Kategorieren unterteilt (Konstanten aus der Headerdatei <math.h>):
Konstante | Kategorie |
FP_NAN |
NAN steht für Not a Number und bedeutet, dass es sich bei dem Wert um keine gültige Gleitpunktdarstellung handelt. |
FP_NORMAL |
eine Gleitpunktzahl in normaler Darstellung |
FP_INFINITE |
Die Gleitpunktzahl wird als unendlicher Wert dargestellt. |
FP_ZERO |
eine Gleitpunktzahl mit dem Wert 0 |
FP_SUBNORMAL |
eine Gleitpunktzahl, mit der besonders kleine Zahlen dargestellt werden können |
Abfragen, in welche Kategorie eine bestimmte Gleitpunktzahl fällt, können Sie mit den folgenden Makros vornehmen:
Makro | Bedeutung |
isnan(x) |
Ist die Gleitpunktzahl gleich FP_NAN, wird 1 zurückgegeben, ansonsten 0. |
isnormal(x) |
Ist die Gleitpunktzahl gleich FP_NORMAL, wird 1 zurückgegeben, ansonsten 0. |
isfinite(x) |
Ist die Gleitpunktzahl eine unendliche Zahl, wird 1 zurückgegeben, ansonsten 0. |
isinf(x) |
Ist die Gleitpunktzahl gleich FP_INFINITE, wird 1 zurückgegeben, ansonsten 0. |
Intern werden alle diese Makros jedoch mithilfe des Makros fpclassify() ausgewertet. Selbstverständlich können Sie fpclassify() auch mithilfe der vordefinierten Konstanten wie folgt verwenden:
if ( fpclassify(x) == FP_NORMAL ) { /* */ } // ... gleichwertig zu ... if( isnormal(x) ) { /* ... */ }
20.3.6 Makro zum Vergleichen von reellen Zahlen 

Makros zum Vergleichen von Gleitpunktzahlen sind nicht unbedingt nötig. In Abschnitt 5.9, »Numerische Gleitpunktprobleme«, haben Sie bereits erfahren, dass abgesehen von Gleichheit (==) alle Vergleiche von Gleitpunktzahlen möglich sind. Dennoch gibt es noch ein Problem, das hier nicht angesprochen wurde: Ist einer der Werte keine gültige Gleitpunktzahl, lassen sich die Operanden nicht miteinander vergleichen und es wird die Exception FE_INVALID ausgelöst. Wollen Sie vermeiden, dass diese Exception ausgelöst wird, müssen Sie die entsprechenden Makros aus der Headerdatei <math.h> verwenden. Die Makros führen einen stillen Vergleich der Operanden durch, womit keine Exception ausgelöst wird. Tabelle 20.7 zeigt die entsprechenden Makros und deren Bedeutung.
Makro | Bedeutung |
isgreater(x, y) |
x größer als y |
isgreaterequal(x, y) |
x größer als oder gleich y |
isless(x, y) |
x kleiner als y |
islessequal(x, y) |
x kleiner als oder gleich y |
islessgreater(x, y) |
x kleiner als y ODER x größer als y |
isunordered(x, y) |
Sind x und y nicht miteinander vergleichbar, gibt dieses Makro 1 zurück, ansonsten 0. |
20.3.7 Zugriff auf die Gleitpunkt-Umgebung – <fenv.h> 

In der Headerdatei <fenv.h> (für Floating Environment = Gleitpunktumgebung) wurden zwei neue Typen, mehrere Makros und einige Funktionen zum Testen auf Fehlersituationen beim Rechnen mit Gleitpunktzahlen implementiert. Diese Umgebung enthält die Systemvariable fexcept_t, die von Gleitpunkt-Exceptions-Funktionen gesetzt werden kann, und die Variable fenv_t, die für den Kontrollmodus (beispielsweise des Rundungsverhaltens) verwendet wird. Die Headerdatei <fenv.h> ist erst seit dem C99-Standard vorhanden.
Um mit dem Programm auf diese Gleitpunkt-Umgebung zuzugreifen, empfiehlt es sich, dies dem Compiler mit dem Pragma STDC FENV_ACCESS mitzuteilen:
// Compiler informieren, damit dieser // Optimierungen verhindert #pragma STDC FENV_ACCESS ON
Mit OFF können Sie das Pragma wieder abschalten. Ob dieses Pragma mit dem Status ON oder OFF implementiert ist, hängt vom Compiler ab. Das Pragma ist ebenfalls in der Headerdatei <fenv.h> definiert.
Zugriff auf die Exception-Statusflags
Für den Zugriff auf die Exception-Statusflags von Gleitpunktzahlen stehen Ihnen mehrere Funktionen zur Verfügung, die in Tabelle 20.8 aufgelistet sind.
Funktion | Beschreibung |
int feclearexcept(int excepts); |
Exception wieder löschen |
int fegetexceptflag( fexcept_t *flagp, int excepts); |
Exception-Statusflags speichern |
int feraiseexcept(int excepts); |
Exception manuell auslösen |
int fesetexceptflag( const fexcept_t *flagp, int excepts); |
Exception-Statusflags wiederherstellen |
int fetestexcept(int excepts); |
Exception-Statusflags testen |
Zur genauen Identifizierung der Exception-Statusflags sind in der Headerdatei <fenv.h> folgende Exceptions als ganzzahlige Makros implementiert:
Makro | Beschreibung |
FE_DIVBYZERO |
Division durch null |
FE_INEXACT |
Ungenauigkeit bei der Gleitpunktberechnung |
FE_INVALID |
ungültige Gleitpunktberechnung |
FE_OVERFLOW |
Überlauf bei der Gleitpunktberechnung |
FE_UNDERFLOW |
Unterlauf bei der Gleitpunktberechnung |
FE_ALL_EXCEPT |
Zugriff auf alle unterstützten Exceptions |
Hierzu ein einfaches Beispiel, in dem wir zunächst alle Exception-Statusflags mit der Funktion fegetexceptflag() sichern. Anschließend lösen wir mit Absicht mit einer Division durch 0 die Exception FE_DIVBYZERO aus, was die Überprüfung mit fetestexcept() auch bestätigt. Anschließend stellen wir die zuvor gesicherte Gleitpunkt-Umgebung mit fesetexceptflag() wieder im Ursprungszustand her. Hier sehen Sie das Listing dazu (das natürlich nur bei C99-konformen Compilern läuft):
/* mathematik3.c */ #include <stdio.h> #include <stdlib.h> /* bei Linux den Compiler-Flag -lm mit angeben */ #include <math.h> #include <fenv.h> #pragma STDC FENV_ACCESS ON int main(void) { double d1=0, d2=1.0, dges; fexcept_t flagp; // Exception-Statusflags sichern fegetexceptflag(&flagp, FE_ALL_EXCEPT ); // mit Absicht eine Division durch 0 auslösen dges = d2 / d1; if( fetestexcept( FE_DIVBYZERO ) ) { printf("Exception ausgelöst: Divsion durch 0 !\n"); } // Exception-Statusflags wiederherstellen fesetexceptflag(&flagp, FE_ALL_EXCEPT); // Sollte jetzt nicht mehr ausgeführt werden. if( fetestexcept( FE_DIVBYZERO ) ) { printf("Exception ausgelöst: Divsion durch 0 !\n"); } return EXIT_SUCCESS; }
Rundungsmodus
Neben den Exception-Statusflags können Sie auch das Rundungsverhalten der Gleitpunktarithmetik mit Funktionen und Makros der Headerdatei <fenv.h> einrichten. Hier sind die beiden dazu nötigen Funktionen:
// Gibt den aktuellen Rundungsmodus zurück, // gewöhnlich ist hier FE_TONEAREST eingestellt. int fegetround(void); // Setzt den Rundungsmodus auf round // Mögliche Modi dazu finden Sie in der Tabelle 20.10. int fesetround(int round);
In der Headerdatei <fenv.h> sind die in Tabelle 20.10 aufgelisteten Makros definiert, die von fegetround() zurückgegeben bzw. mit fesetround() gesetzt werden können.
Makro | Beschreibung |
FE_DOWNWARD |
Gleitpunktzahl zum nächsten Integer abrunden |
FE_UPWARD |
Gleitpunktzahl zum nächsten Integer aufrunden |
FE_TONEAREST |
Gleitpunktzahl auf nächstmöglichen Integer auf- oder abrunden (Standardeinstellung) |
FE_TOWARDZERO |
Gleitpunktzahl auf den nächstmöglichen Integer in der Nähe von 0 runden. |
Die Verwendung ist entsprechend einfach:
#include <fenv.h> ... int save; ... // Rundungsmodus sichern save = fegetround(); // neuen Rundungsmodus festlegen fesetround(FE_UPWARD); ... ... // Rundungsmodus wiederherstellen fesetround(save);
Zugriff auf die komplette Gleitpunktumgebung
Es gibt auch noch Funktionen, mit denen Sie auf die komplette Gleitpunkt-Umgebung zugreifen können:
// Gleitpunkt-Umgebung speichern int fegetenv(fenv_t *envp); // Gleitpunkt-Umgebung in einen Non-Stop-Modus setzen. // Hierbei unterbricht eine Exception nicht die // Programmausführung. int feholdexcept(fenv_t *envp); // Gleitpunkt-Umgebung wiederherstellen int fesetenv(const fenv_t *envp); // eine gespeicherte Gleitpunkt-Umgebung wiederherstellen // und alle zur Laufzeit gesetzten Exceptions erneut auslösen int feupdateenv(const fenv_t *envp);
Ihre Meinung
Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.