Menu

Mathematikfunktionen in C: math.h, sqrt, pow und das Linken mit -lm

Eine Tour durch math.h - sqrt, pow, fabs, floor und ceil, round, fmod, die Trigonometrie- und Logarithmusfamilien, INFINITY und NAN - dazu das Linker-Flag -lm, das den klassischen undefined-reference-Fehler verursacht.

Diese Seite enthält ausführbare Editoren - bearbeiten, ausführen und Ausgabe sofort sehen.

Cs Kernsprache gibt dir + - * / und %, sonst nichts - keinen Potenzoperator, keine Quadratwurzel, kein Runden. Alles jenseits der Arithmetik lebt in der Standard-Mathematikbibliothek, deklariert in <math.h>.

Diese Funktionen arbeiten alle mit double-Genauigkeit: Sie nehmen double-Argumente entgegen und liefern double. Das ist das Erste, was man verinnerlichen sollte, denn ein int zu übergeben funktioniert stillschweigend (es wird umgewandelt), während das Ergebnis einem int zuzuweisen still den Bruchteil verwirft.

Der Einstieg und das -lm-Flag

Auf deiner eigenen Maschine kann das Kompilieren fehlschlagen:

/usr/bin/ld: /tmp/ccXYZ.o: in function `main':
program.c:(.text+0x1a): undefined reference to `sqrt'
collect2: error: ld returned 1 exit status

Das ist der mit Abstand häufigste Stolperstein bei math.h, und es ist kein Fehler in deinem Code. Der Header lieferte die Deklarationen, der Compiler war also zufrieden; die Implementierungen liegen in einer eigenen Bibliothek (libm), die der Linker unter Linux standardmäßig nicht einbezieht. Füge -lm hinzu:

gcc program.c -o program -lm

Das Flag muss nach den Quelldateien kommen - Linker verarbeiten ihre Eingaben der Reihe nach, und eine Bibliothek, die vor dem Code steht, der sie braucht, löst nichts auf. Unter macOS und mit MinGW unter Windows gehören die Mathematikroutinen bereits zur Standard-C-Bibliothek, -lm ist dort also unnötig (unter macOS aber harmlos).

Potenzen und Wurzeln

sqrt(x) einer negativen Zahl liefert NaN, statt abzustürzen; sichere das Argument ab, wenn es negativ sein kann. hypot ist wissenswert, weil es denselben Wert wie sqrt(x*x + y*y) berechnet, ohne überzulaufen, wenn x oder y sehr groß ist.

Für kleine ganzzahlige Potenzen ist schlichte Multiplikation sowohl schneller als auch exakt - x * x schlägt pow(x, 2.0), das über Logarithmen geht und 8.999999999999998 liefern kann, wo du 9 erwartet hast.

Runden: floor, ceil, round, trunc

Vier Funktionen, vier verschiedene Antworten bei negativen Zahlen. Diese Tabelle ist der merkenswerte Teil:

  • floor(x) - größte ganze Zahl, die nicht größer als x ist. Bewegt sich immer Richtung negativ unendlich: floor(-2.3) ist -3.
  • ceil(x) - kleinste ganze Zahl, die nicht kleiner als x ist. Bewegt sich immer Richtung positiv unendlich: ceil(-2.3) ist -2.
  • round(x) - nächste ganze Zahl, Hälften von null weg: round(2.5) ist 3, round(-2.5) ist -3.
  • trunc(x) - schneidet den Bruchteil ab, Richtung null: trunc(-2.7) ist -2.

Alle vier liefern ein double. Nach int zu casten, wenn du eine Ganzzahl willst, ist in Ordnung, aber beachte, dass (int)x für sich abschneidet - (int)(x + 0.5) ist also das alte „Rundungs"-Idiom, und es ist für negative Zahlen falsch. Nimm round().

fabs und fmod

fabs ist der Absolutbetrag für Gleitkommazahlen. Stattdessen zu abs zu greifen ist ein klassischer Fehler, denn abs nimmt ein int und die Umwandlung passiert stillschweigend:

% funktioniert bei double überhaupt nicht - es ist ein Ganzzahloperator, und der Compiler weist es zurück. fmod(a, b) ist sein Gleitkomma-Gegenstück, und es behält das Vorzeichen von a.

fabs ist auch der richtige Weg, zwei doubles zu vergleichen, denn == ist bei Gleitkommazahlen eine Falle:

0.1 und 0.2 lassen sich binär nicht exakt darstellen, ihre Summe liegt also haarscharf neben 0.3. Vergleiche mit einer Toleranz, nie mit ==.

Trigonometrie

Die trigonometrischen Funktionen arbeiten im Bogenmaß, nicht in Grad - die andere verlässliche Quelle falscher Antworten.

Der vollständige Satz: sin, cos, tan, ihre Umkehrungen asin, acos, atan, das zweiargumentige atan2(y, x) und die hyperbolische Familie sinh, cosh, tanh. Bevorzuge für Winkel atan2(y, x) gegenüber atan(y / x) - es behandelt x == 0 und trifft den Quadranten richtig.

math.h definiert auf den meisten Systemen M_PI, aber das ist eine POSIX-Erweiterung statt Standard-C, unter -std=c17 kann es also fehlen. Eine eigene Konstante zu definieren, wie oben, ist portabel.

Logarithmen und Exponentialfunktionen

Beachte die aus der Mathematik übernommene Namensfalle: log() ist der natürliche Logarithmus (Basis e), nicht Basis 10. Basis 10 ist log10().

log(0.0) liefert negativ unendlich und log(-1.0) liefert NaN - keines von beidem stürzt ab, was bedeutet, dass ein schlechtes Argument still durch deine Rechnung reist, bis irgendetwas nan ausgibt.

INFINITY, NAN und die Prüfung darauf

Gleitkommazahlen kennen Werte, die keine Zahlen sind, und math.h benennt sie:

Die letzte Zeile ist die entscheidende. Du kannst nicht if (x == NAN) schreiben - es ist immer falsch. Nimm isnan(x). (Die Identität x != x ist nur für NaN wahr, so wird isnan oft implementiert, und es ist ein praktischer Ersatz, falls dir je eine Werkzeugkette ohne begegnet.)

Unendlichkeiten entstehen durch Überlauf (exp(1000.0)) und durch die Division eines double ungleich null durch null, die - anders als die Ganzzahldivision durch null - definiert ist und nicht abstürzt.

Die float- und long-double-Varianten

Jede Funktion hat drei Formen: die double-Fassung, eine mit f-Suffix für float und eine mit l-Suffix für long double.

float       sqrtf(float x);
double      sqrt(double x);
long double sqrtl(long double x);

Nimm die schlichten double-Fassungen, sofern du keinen gemessenen Grund dagegen hast - double ist das, wozu C standardmäßig befördert, und float-Varianten hineinzumischen kostet meist mehr an Umwandlungen, als es spart. Die eine Stelle, an der das Suffix zählt, ist eine reine float-Rechnung, die auf Hardware ohne Double-Unterstützung leistungskritisch ist.

Ein praktisches Beispiel

Mehrere davon zusammen - der Abstand zweier Punkte und die Lösungen einer quadratischen Gleichung:

Beachte fabs(disc) < 1e-12 statt disc == 0.0 - dieselbe Toleranzregel wie zuvor, angewandt dort, wo eine aus Messwerten berechnete Diskriminante fast nie exakt auf null landet.

<math.h> ist ein Header in einem größeren Werkzeugkasten; die Seite Standardbibliothek kartiert den Rest, und Zufallszahlen behandelt rand, das in <stdlib.h> lebt statt hier.

Häufig gestellte Fragen

Wie benutze ich Mathematikfunktionen in C?

Setze #include <math.h> an den Anfang der Datei und rufe sie auf: sqrt(16.0), pow(2.0, 10.0), fabs(-3.5). Sie nehmen double entgegen und liefern double. Unter Linux musst du zusätzlich die Mathematikbibliothek mit -lm linken: gcc program.c -o program -lm.

Warum bekomme ich beim Kompilieren „undefined reference to sqrt"?

Der Header hat dem Compiler die Deklaration gegeben, aber die Implementierung liegt in einer eigenen Bibliothek, die der Linker unter Linux nicht automatisch hinzunimmt. Füge -lm am Ende des Befehls hinzu: gcc program.c -o program -lm. Unter macOS und mit MinGW unter Windows gehört der Mathematikcode zur Standard-C-Bibliothek, dort braucht es kein Flag.

Was ist der Unterschied zwischen abs und fabs in C?

abs() aus <stdlib.h> nimmt ein int und liefert ein int. fabs() aus <math.h> nimmt ein double und liefert ein double. abs(-3.7) aufzurufen wandelt das Argument zuerst in int um und ergibt 3, wobei der Bruchteil still verworfen wird - nimm für Gleitkommawerte fabs.

Wie prüfe ich in C auf NaN?

Benutze isnan(x) aus <math.h>. Mit x == NAN kannst du nicht prüfen, denn NaN vergleicht sich mit allem als ungleich, auch mit sich selbst - diese Eigenheit ist tatsächlich der Ersatztest: x != x ist nur für NaN wahr. Nimm isinf(x) für Unendlichkeiten und isfinite(x), um auf eine gewöhnliche Zahl zu prüfen.

Coddy programming languages illustration

Lerne mit Coddy zu programmieren

LOS GEHT'S