Menu

Math Functions in C: math.h, sqrt, pow, and Linking with -lm

A tour of math.h - sqrt, pow, fabs, floor and ceil, round, fmod, the trig and log families, INFINITY and NAN - plus the -lm linker flag that causes the classic undefined reference error.

This page includes runnable editors - edit, run, and see output instantly.

C's core language gives you + - * / and %, and nothing else - no power operator, no square root, no rounding. Everything beyond arithmetic lives in the standard math library, declared in <math.h>.

These functions all work in double precision: they take double arguments and return double. That is the first thing to internalize, because passing an int works silently (it is converted) while assigning the result to an int silently discards the fraction.

Getting Started, and the -lm Flag

On your own machine, compiling that may fail:

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

This is the single most common stumble with math.h, and it is not a mistake in your code. The header supplied the declarations, so the compiler was satisfied; the implementations live in a separate library (libm) that the linker does not include by default on Linux. Add -lm:

gcc program.c -o program -lm

The flag must come after the source files - linkers process their inputs in order, and a library listed before the code that needs it resolves nothing. On macOS and with MinGW on Windows, the math routines are part of the standard C library already, so -lm is unnecessary (though harmless on macOS).

Powers and Roots

sqrt(x) of a negative number returns NaN rather than crashing; guard the argument if it can be negative. hypot is worth knowing because it computes the same value as sqrt(x*x + y*y) without overflowing when x or y is very large.

For small integer powers, plain multiplication is both faster and exact - x * x beats pow(x, 2.0), which goes through logarithms and can return 8.999999999999998 where you expected 9.

Rounding: floor, ceil, round, trunc

Four functions, four different answers for negative numbers. That table is the part worth memorizing:

  • floor(x) - largest integer not greater than x. Always moves toward negative infinity: floor(-2.3) is -3.
  • ceil(x) - smallest integer not less than x. Always moves toward positive infinity: ceil(-2.3) is -2.
  • round(x) - nearest integer, halves away from zero: round(2.5) is 3, round(-2.5) is -3.
  • trunc(x) - chop the fractional part, moving toward zero: trunc(-2.7) is -2.

All four return a double. Casting to int when you want an integer is fine, but note that (int)x on its own performs truncation - so (int)(x + 0.5) is the old "round" idiom, and it is wrong for negative numbers. Use round().

fabs and fmod

fabs is absolute value for floating point. Reaching for abs instead is a classic mistake, because abs takes an int and the conversion happens silently:

% does not work on double at all - it is an integer operator and the compiler rejects it. fmod(a, b) is its floating-point counterpart, and it keeps the sign of a.

fabs is also the correct way to compare two doubles, since == on floating point is a trap:

0.1 and 0.2 cannot be represented exactly in binary, so their sum is a hair away from 0.3. Compare with a tolerance, never with ==.

Trigonometry

The trig functions work in radians, not degrees - the other reliable source of wrong answers.

The full set: sin, cos, tan, their inverses asin, acos, atan, the two-argument atan2(y, x), and the hyperbolic family sinh, cosh, tanh. Prefer atan2(y, x) over atan(y / x) for angles - it handles x == 0 and gets the quadrant right.

math.h defines M_PI on most systems, but it is a POSIX extension rather than standard C, so it may be missing under -std=c17. Defining your own constant, as above, is portable.

Logarithms and Exponentials

Note the naming trap carried over from mathematics: log() is the natural logarithm (base e), not base 10. Base 10 is log10().

log(0.0) returns negative infinity and log(-1.0) returns NaN - neither one crashes, which means a bad argument travels silently through your calculation until something prints nan.

INFINITY, NAN, and Checking for Them

Floating point has values that are not numbers, and math.h names them:

That last line is the crucial one. You cannot write if (x == NAN) - it is always false. Use isnan(x). (The identity x != x is true only for NaN, which is how isnan is often implemented and a handy fallback if you ever meet a toolchain without it.)

Infinities arrive from overflow (exp(1000.0)) and from division of a nonzero double by zero, which - unlike integer division by zero - is defined and does not crash.

float and long double Variants

Every function has three forms: the double version, an f-suffixed float version, and an l-suffixed long double version.

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

Use the plain double versions unless you have a measured reason not to - double is what C promotes to by default, and mixing float variants in usually costs more in conversions than it saves. The one place the suffix matters is when a float-only calculation is performance-critical on hardware without double support.

A Practical Example

Putting several of these together - the distance between two points and the roots of a quadratic:

Notice fabs(disc) < 1e-12 rather than disc == 0.0 - the same tolerance rule as before, applied where a discriminant computed from measured values will almost never land exactly on zero.

<math.h> is one header in a larger toolkit; the standard library page maps the rest, and random numbers covers rand, which lives in <stdlib.h> rather than here.

Frequently Asked Questions

How do I use math functions in C?

Add #include <math.h> at the top of the file and call them: sqrt(16.0), pow(2.0, 10.0), fabs(-3.5). They take and return double. On Linux you must also link the math library with -lm: gcc program.c -o program -lm.

Why do I get 'undefined reference to sqrt' when compiling?

The header gave the compiler the declaration, but the implementation lives in a separate library the linker does not pull in automatically on Linux. Add -lm at the end of the command: gcc program.c -o program -lm. On macOS and with MinGW on Windows the math code is part of the standard C library, so no flag is needed.

What is the difference between abs and fabs in C?

abs() from <stdlib.h> takes an int and returns an int. fabs() from <math.h> takes a double and returns a double. Calling abs(-3.7) converts the argument to int first and gives 3, silently discarding the fraction - use fabs for floating-point values.

How do I check for NaN in C?

Use isnan(x) from <math.h>. You cannot test with x == NAN, because NaN compares unequal to everything including itself - that quirk is actually the fallback test: x != x is true only for NaN. Use isinf(x) for infinities and isfinite(x) to check for an ordinary number.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED