math.h in C: Complete Function Reference with Examples

math.h declares C's standard math functions: sqrt, pow, sin, log, floor and more. A complete reference with compiled examples, C99 additions and fixes.

math.h in C: reference of standard math functions with square root, pi, sine and exponential symbols

Every C programmer meets math.h in their first week and keeps using it for the rest of their career — and yet the two most common math.h questions on the internet are not about mathematics at all. They are “why do I get undefined reference to sqrt?” and “why did my square root come out as zero?”

This is a complete, modern reference for the C math library: every function family with prototypes and descriptions, working examples for the ones you will actually use, the C99 additions most old tutorials skip, and the handful of gotchas behind those two famous questions. Every code example here compiles clean with gcc -std=c11 -Wall -Wextra -lm and runs exactly as shown — including the error messages, which were captured from real failed builds.

Table of Contents

What Is math.h in C?

math.h is the header of the C standard library’s mathematics functions. It declares functions for powers and roots (sqrt, pow, cbrt), trigonometry (sin, cos, tan, atan2), exponentials and logarithms (exp, log, log2), rounding (floor, ceil, round, trunc), and floating-point classification (isnan, isinf) — operating on double by default, with float and long double variants.

Two facts old tutorials get wrong:

  • math.h is standard C, not a compiler extra. It has been part of the language standard since C89 and works identically in GCC, Clang, MSVC, and every conforming compiler — it was never something “provided by Turbo C.”
  • It grew substantially in C99. Functions like round, trunc, cbrt, log2, hypot, fma, and the whole isnan/isinf family arrived in C99, along with float (sinf) and long double (sinl) variants of everything. Any reference that stops at the C89 function list is describing a 35-year-old snapshot.

Compiling: Don’t Forget -lm

Before the reference — the number one math.h problem in practice. On Linux, the math functions live in a separate library (libm), so you must link it explicitly with -lm:

gcc -std=c11 -Wall -Wextra program.c -o program -lm

Forget the -lm and you get the most-searched C linker error there is:

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

Note what this is not: it is not a missing header, and no amount of #include <math.h> fixes it. The header declares the functions; -lm supplies their compiled implementations at link time. (Windows toolchains like MSVC link the math library automatically, which is why the same code “just works” there.)

math.h Function Reference

All functions below take and return double unless noted. Since C99, each also has an f suffix variant for float (sqrtf) and an l suffix variant for long double (sqrtl). If the differences between these floating-point types are fuzzy, our guide to basic data types in C covers them.

Powers and roots

FunctionPrototypeReturns
sqrtdouble sqrt(double x)Square root of x (x ≥ 0)
cbrtdouble cbrt(double x)Cube root of x — works for negative x too (C99)
powdouble pow(double x, double y)x raised to the power y
hypotdouble hypot(double x, double y)√(x²+y²) without intermediate overflow (C99)
#include <stdio.h>
#include <math.h>

int main(void)
{
    double area = 36.0;
    printf("sqrt(36.0)      = %f\n", sqrt(area));      /* square root  */
    printf("cbrt(27.0)      = %f\n", cbrt(27.0));      /* cube root    */
    printf("pow(2.0, 10.0)  = %f\n", pow(2.0, 10.0));  /* 2 to the 10  */
    printf("hypot(3.0, 4.0) = %f\n", hypot(3.0, 4.0)); /* sqrt(3*3+4*4)*/
    return 0;
}

Output:

sqrt(36.0)      = 6.000000
cbrt(27.0)      = 3.000000
pow(2.0, 10.0)  = 1024.000000
hypot(3.0, 4.0) = 5.000000

Prefer hypot(x, y) over sqrt(x*x + y*y) for distances: it gives the same answer but cannot overflow on large inputs.

Trigonometric functions

FunctionPrototypeReturns
sin, cos, tandouble sin(double x)Sine/cosine/tangent of x, x in radians
asin, acos, atandouble asin(double x)Inverse functions, result in radians
atan2double atan2(double y, double x)Angle of point (x, y) — knows the quadrant, unlike atan(y/x)

Every trig function works in radians, not degrees — forgetting this is the classic trig bug. Convert with radians = degrees × π / 180:

#include <stdio.h>
#include <math.h>

int main(void)
{
    double pi = acos(-1.0);   /* portable way to get pi at full precision */

    printf("sin(pi/6) = %f\n", sin(pi / 6));   /* 30 degrees -> 0.5   */
    printf("cos(pi/3) = %f\n", cos(pi / 3));   /* 60 degrees -> 0.5   */
    printf("tan(pi/4) = %f\n", tan(pi / 4));   /* 45 degrees -> 1.0   */

    /* atan2(y, x) knows the quadrant; atan(y/x) does not */
    printf("atan2(1.0, -1.0) = %f radians\n", atan2(1.0, -1.0));

    /* converting degrees to radians */
    double degrees = 90.0;
    printf("sin(%g degrees) = %f\n", degrees, sin(degrees * pi / 180.0));
    return 0;
}

Output:

sin(pi/6) = 0.500000
cos(pi/3) = 0.500000
tan(pi/4) = 1.000000
atan2(1.0, -1.0) = 2.356194 radians
sin(90 degrees) = 1.000000

Why acos(-1.0) instead of M_PI? Because M_PI is not part of standard C — it comes from POSIX. Compile with strict -std=c11 and M_PI produces error: 'M_PI' undeclared (verified); on MSVC it needs #define _USE_MATH_DEFINES. acos(-1.0) works everywhere.

The hyperbolic family follows the same naming: sinh, cosh, tanh (C89) and their inverses asinh, acosh, atanh (C99).

Exponential and logarithmic functions

FunctionPrototypeReturns
expdouble exp(double x)e raised to the power x
exp2double exp2(double x)2 raised to the power x (C99)
logdouble log(double x)Natural logarithm ln(x), x > 0
log10double log10(double x)Base-10 logarithm
log2double log2(double x)Base-2 logarithm (C99)
frexpdouble frexp(double x, int *exp)Splits x into mantissa × 2^exponent
ldexpdouble ldexp(double x, int exp)Computes x × 2^exponent
#include <stdio.h>
#include <math.h>

int main(void)
{
    printf("exp(1.0)     = %f   (e itself)\n", exp(1.0));
    printf("log(100.0)   = %f   (natural log, base e)\n", log(100.0));
    printf("log10(100.0) = %f   (base 10)\n", log10(100.0));
    printf("log2(8.0)    = %f   (base 2, added in C99)\n", log2(8.0));

    /* any other base via the change-of-base formula */
    double base = 5.0, x = 125.0;
    printf("log base 5 of 125 = %f\n", log(x) / log(base));
    return 0;
}

Output:

exp(1.0)     = 2.718282   (e itself)
log(100.0)   = 4.605170   (natural log, base e)
log10(100.0) = 2.000000   (base 10)
log2(8.0)    = 3.000000   (base 2, added in C99)
log base 5 of 125 = 3.000000

Rounding and remainder functions

FunctionPrototypeBehavior
floordouble floor(double x)Rounds toward −infinity (always down)
ceildouble ceil(double x)Rounds toward +infinity (always up)
rounddouble round(double x)Nearest integer; halfway cases away from zero (C99)
truncdouble trunc(double x)Drops the fraction — rounds toward zero (C99)
lroundlong lround(double x)Like round, but returns a long (C99)
modfdouble modf(double x, double *ipart)Splits x into integer and fractional parts
fmoddouble fmod(double x, double y)Floating-point remainder of x/y, sign of x
fabsdouble fabs(double x)Absolute value

The four rounding functions differ only on which direction they round — and the differences only show for negative numbers and halfway cases:

floor vs ceil vs round vs trunc in C floor(x) toward -infinity 2.7 → 2.0 -2.7 → -3.0 always rounds down ceil(x) toward +infinity 2.7 → 3.0 -2.7 → -2.0 always rounds up round(x) to nearest integer 2.7 → 3.0 -2.7 → -3.0 halfway goes away from 0 trunc(x) toward zero 2.7 → 2.0 -2.7 → -2.0 just drops the fraction round() and trunc() were added in C99 — floor() and ceil() have been there since C89. All return a double.
#include <stdio.h>
#include <math.h>

int main(void)
{
    double values[] = { 2.3, 2.7, -2.3, -2.7 };

    printf("value   floor   ceil   round   trunc\n");
    for (int i = 0; i < 4; i++) {
        double v = values[i];
        printf("%5.1f  %6.1f %6.1f  %6.1f  %6.1f\n",
               v, floor(v), ceil(v), round(v), trunc(v));
    }

    /* modf splits a number into integer and fractional parts */
    double intpart;
    double frac = modf(3.75, &intpart);
    printf("\nmodf(3.75): integer part = %g, fraction = %g\n", intpart, frac);
    return 0;
}

Output:

value   floor   ceil   round   trunc
  2.3     2.0    3.0     2.0     2.0
  2.7     2.0    3.0     3.0     2.0
 -2.3    -3.0   -2.0    -2.0    -2.0
 -2.7    -3.0   -2.0    -3.0    -2.0

modf(3.75): integer part = 3, fraction = 0.75

For remainders of floating-point division, use fmod — the % operator only works on integers in C (our modulus operator guide covers the integer side).

Classification macros and special values (C99)

Floating-point math doesn’t stop at ordinary numbers — division by zero, overflow, and invalid operations produce infinities and NaN (“not a number”), and C99 gave the tools to detect them:

MacroReturns non-zero when…
isnan(x)x is NaN
isinf(x)x is +∞ or −∞
isfinite(x)x is an ordinary, finite number
signbit(x)x is negative (works even for −0.0)

Plus the constants INFINITY, NAN, and HUGE_VAL. You will see them in action in the gotchas program below.

Common math.h Errors and Gotchas

Four mistakes account for most math.h bug reports — here they all are in one compiled program:

#include <stdio.h>
#include <stdlib.h>   /* abs() lives HERE, not in math.h */
#include <math.h>

int main(void)
{
    /* Gotcha 1: abs() is for ints -- fabs() is for doubles */
    printf("abs(-7)        = %d\n", abs(-7));
    printf("fabs(-7.9)     = %f\n", fabs(-7.9));

    /* Gotcha 2: integer division happens BEFORE the call */
    printf("sqrt(1 / 2)    = %f   <-- 1/2 is int division = 0\n", sqrt(1 / 2));
    printf("sqrt(1.0 / 2)  = %f\n", sqrt(1.0 / 2));

    /* Gotcha 3: domain errors return NaN, range errors return infinity */
    double bad = sqrt(-1.0);
    double big = exp(1000.0);
    printf("sqrt(-1.0)     = %f, isnan = %d\n", bad, isnan(bad));
    printf("exp(1000.0)    = %f, isinf = %d\n", big, isinf(big));

    /* Gotcha 4: fmod keeps the sign of the FIRST argument */
    printf("fmod(5.5, 2.0)  = %f\n", fmod(5.5, 2.0));
    printf("fmod(-5.5, 2.0) = %f\n", fmod(-5.5, 2.0));
    return 0;
}

Output:

abs(-7)        = 7
fabs(-7.9)     = 7.900000
sqrt(1 / 2)    = 0.000000   <-- 1/2 is int division = 0
sqrt(1.0 / 2)  = 0.707107
sqrt(-1.0)     = -nan, isnan = 1
exp(1000.0)    = inf, isinf = 1
fmod(5.5, 2.0)  = 1.500000
fmod(-5.5, 2.0) = -1.500000

Unpacking each:

  1. abs() vs fabs(). abs() is declared in stdlib.h and works on int — call it on a double and the value silently truncates. For floating point, use fabs() from math.h.
  2. Integer division sneaks in before the call. sqrt(1 / 2) computes 1 / 2 as integer division first — result 0 — then takes the square root of 0. Make at least one operand floating point: sqrt(1.0 / 2).
  3. Errors come back as values. A domain error like sqrt(-1.0) returns NaN; a range error like exp(1000.0) returns infinity (and both set errno to EDOM/ERANGE on typical implementations). Check with isnan()/isinf() rather than hoping.
  4. fmod takes its sign from the first argumentfmod(-5.5, 2.0) is −1.5, not 0.5. If that surprises you in modular arithmetic, C99’s remainder() uses a different (round-to-nearest) convention.
  • tgmath.h (C99) — type-generic math: after including it, plain sqrt(x) automatically dispatches to sqrtf, sqrt, or sqrtl based on the argument type.
  • complex.h (C99) — complex-number arithmetic: the double complex type plus csqrt, cexp, cabs, and complex versions of the whole function set.
  • stdlib.h — home of the integer absolute values (abs, labs, llabs) and integer division with remainder (div, ldiv).
  • fenv.h (C99) — control of the floating-point environment: rounding modes and exception flags, for when you need to detect overflow the rigorous way.
  • float.h — the limits of your floating types (DBL_EPSILON, DBL_MAX), essential reading once you start comparing doubles for “equality.”

Key Takeaways

  • math.h is standard C — in every conforming compiler since C89, and substantially extended in C99 with round, trunc, cbrt, log2, hypot, the isnan/isinf classification family, and float/long double variants of everything.
  • On Linux/GCC, always link with -lmundefined reference to sqrt means a missing library, not a missing header.
  • Trig functions work in radians; convert degrees with deg × π / 180, and get π portably via acos(-1.0) because M_PI is POSIX, not standard C.
  • The rounding four: floor (down), ceil (up), round (nearest, halves away from zero), trunc (toward zero) — they only disagree on negatives and halves.
  • abs() (stdlib.h) is for ints; fabs() (math.h) is for doubles — and watch for integer division inside call arguments.
  • Domain and range errors return NaN and infinity rather than crashing — check results with isnan() and isinf().

Frequently Asked Questions

Conclusion

The pattern behind every gotcha in this reference is the same one: math.h is honest about being floating-point mathematics, not the idealized kind. Results come back as approximations, errors come back as special values, and the C type system happily performs integer division before your beautifully chosen function ever runs. Learn to read those signals — a NaN here, a suspicious 0.000000 there — and the library becomes completely predictable.

From here, the natural next step is understanding the functions you will wrap around these: parameters, return values, and how C passes data around, covered in our guide to functions in C. And when a result looks almost right instead of wrong, that is floating-point representation itself talking — a topic worth a whole article of its own.

Scroll to Top