C math.h Library
The C standard library’s <math.h> header gives your programs access to a full set of mathematical functions — square roots, powers, trigonometry, logarithms, and rounding — all operating on floating-point values. Without it, you would have to hand-write approximations for operations the C library already implements efficiently and accurately using the CPU’s floating-point hardware. Nearly every C program that does numeric work, from graphics and games to physics simulations and financial calculations, relies on math.h.
Overview / How math.h Works
math.h is a header file: it only declares function prototypes. The actual machine code that implements sqrt, sin, pow, and the rest lives in a separate library called libm on Linux and most Unix-like systems (on Windows, the math functions are usually bundled directly into the C runtime). This means that on gcc or clang you must explicitly link the math library with the -lm flag:
gcc program.c -o program -lm
If you forget -lm, the code will usually still compile, but the linker will fail with an error like undefined reference to 'sqrt', because the compiler found the declaration in the header but could not find the compiled function body.
Almost every function in math.h operates on double (double-precision, 64-bit IEEE 754 floating point, roughly 15–17 significant decimal digits). Most functions also have float and long double variants distinguished by an f or l suffix — for example sqrtf(float) and sqrtl(long double). Using the correctly suffixed function for your argument’s type avoids unnecessary conversions and keeps precision consistent.
Functions in math.h generally fall into a few families:
- Power and root:
pow,sqrt,cbrt - Exponential and logarithmic:
exp,log,log10,log2 - Trigonometric:
sin,cos,tan,asin,acos,atan,atan2 - Hyperbolic:
sinh,cosh,tanh - Rounding and remainder:
floor,ceil,round,trunc,fmod - Classification:
isnan,isinf,isfinite
Some math functions have a restricted domain. For example, sqrt is only defined for non-negative numbers, and log is only defined for positive numbers. When you pass an invalid argument, the function does not stop your program — instead it returns a special IEEE 754 value called NaN (Not a Number) and sets the global variable errno (declared in <errno.h>) to EDOM (domain error). Similarly, if a result is too large to represent as a double, the function returns HUGE_VAL (or the built-in INFINITY constant) and sets errno to ERANGE (range error). Checking errno is the standard way to detect these failures, since the function’s return value alone can’t distinguish “a huge but valid number” from “an overflow.”
Syntax
The general form of a math.h call is simple — pass one or more numeric arguments and receive a double result:
#include <math.h>
double result = function_name(argument1, argument2 /* , ... */);
| Function | Signature | Description |
|---|---|---|
sqrt |
double sqrt(double x) |
Square root of x (domain: x >= 0) |
pow |
double pow(double base, double exp) |
Raises base to the power exp |
fabs |
double fabs(double x) |
Absolute value of a floating-point number |
ceil |
double ceil(double x) |
Rounds up to the nearest integer value |
floor |
double floor(double x) |
Rounds down to the nearest integer value |
round |
double round(double x) |
Rounds to the nearest integer, halves away from zero |
fmod |
double fmod(double x, double y) |
Floating-point remainder of x / y |
sin/cos/tan |
double sin(double x) |
Trigonometric functions; argument is in radians |
log |
double log(double x) |
Natural logarithm (base e), domain: x > 0 |
Examples
Example 1: Basic power, root, and rounding functions
#include <stdio.h>
#include <math.h>
int main(void) {
double x = 16.0;
double y = 2.0;
printf("sqrt(%.1f) = %.2f\n", x, sqrt(x));
printf("pow(%.1f, %.1f) = %.2f\n", x, y, pow(x, y));
printf("fabs(-9.5) = %.2f\n", fabs(-9.5));
printf("ceil(4.3) = %.2f\n", ceil(4.3));
printf("floor(4.8) = %.2f\n", floor(4.8));
return 0;
}
Output:
sqrt(16.0) = 4.00
pow(16.0, 2.0) = 256.00
fabs(-9.5) = 9.50
ceil(4.3) = 5.00
floor(4.8) = 4.00
This example shows the most commonly used math functions. sqrt(16.0) returns 4.00 because 4 × 4 = 16. pow(16.0, 2.0) raises 16 to the power of 2, giving 256. fabs strips the sign from a negative number, and ceil/floor round toward positive and negative infinity respectively — notice they round to the nearest integer in that direction, not simply truncate.
Example 2: Trigonometric functions and degree-to-radian conversion
All trigonometric functions in math.h expect their argument in radians, not degrees. A common mistake is passing a raw degree value straight into sin or cos. Convert degrees to radians first using the formula radians = degrees × (PI / 180):
#include <stdio.h>
#include <math.h>
#define PI 3.14159265358979323846
int main(void) {
double angle_deg = 60.0;
double angle_rad = angle_deg * (PI / 180.0);
printf("sin(60 deg) = %.4f\n", sin(angle_rad));
printf("cos(60 deg) = %.4f\n", cos(angle_rad));
printf("tan(60 deg) = %.4f\n", tan(angle_rad));
return 0;
}
Output:
sin(60 deg) = 0.8660
cos(60 deg) = 0.5000
tan(60 deg) = 1.7321
Here we define our own PI constant for portability (some compilers only expose the non-standard M_PI macro when certain feature-test macros are defined). The results match the well-known exact values: sin(60°) = √3/2 ≈ 0.8660, cos(60°) = 0.5, and tan(60°) = √3 ≈ 1.7321.
Example 3: Error handling with errno, isinf, round, and fmod
#include <stdio.h>
#include <math.h>
#include <errno.h>
int main(void) {
errno = 0;
double result = sqrt(-4.0);
if (errno == EDOM) {
printf("Domain error: cannot take sqrt of a negative number\n");
} else {
printf("sqrt(-4.0) = %.2f\n", result);
}
double big = pow(10.0, 400.0);
if (isinf(big)) {
printf("pow(10, 400) overflowed to infinity\n");
}
printf("round(2.5) = %.1f\n", round(2.5));
printf("fmod(10.0, 3.0) = %.2f\n", fmod(10.0, 3.0));
return 0;
}
Output:
Domain error: cannot take sqrt of a negative number
pow(10, 400) overflowed to infinity
round(2.5) = 3.0
fmod(10.0, 3.0) = 1.00
This example demonstrates defensive error handling. We reset errno to 0 before the call (it is not automatically cleared), then check whether sqrt set it to EDOM. We also use isinf to detect that 10400 is far beyond what a double can represent, so it becomes infinity. Finally, round(2.5) shows that C rounds halves away from zero (giving 3.0, not 2.0), and fmod computes a true floating-point remainder, unlike the % operator which only works on integers.
Under the Hood
When you call sqrt(x), several things happen in sequence:
- The compiler sees the prototype
double sqrt(double)from<math.h>and generates a call instruction (or, with optimizations enabled, may emit a direct hardware square-root instruction likesqrtsdon x86-64, bypassing the function call entirely). - If a real function call is generated, the linker resolves the symbol
sqrtagainst libm, which contains a highly optimized, tested implementation (often written in assembly for critical functions). - The CPU’s floating-point unit performs the computation in IEEE 754 double precision, which stores numbers using 1 sign bit, 11 exponent bits, and 52 mantissa bits.
- If the input is invalid for the function’s mathematical domain, the implementation sets
errnoand returns a bit pattern representing NaN, which then propagates through any further arithmetic (any operation involving NaN produces NaN).
This is why a single invalid math operation deep in a calculation can silently corrupt an entire result chain unless you check for it.
Common Mistakes
1. Forgetting to link the math library
gcc program.c -o program
This fails at link time with undefined reference to 'sqrt' on many Linux toolchains, because the declaration is found but the implementation is not. Fix it by linking -lm:
gcc program.c -o program -lm
2. Passing raw degrees to trigonometric functions
/* Wrong: sin expects radians, not degrees */
double result = sin(90.0); /* Not sin of 90 degrees! */
Since sin expects radians, sin(90.0) computes the sine of 90 radians (a huge angle after wrapping), not 90 degrees. Convert first:
double result = sin(90.0 * (PI / 180.0));
3. Comparing floating-point results with ==
/* Wrong: floating point results are rarely exactly equal */
if (sqrt(2.0) * sqrt(2.0) == 2.0) {
printf("Equal\n");
}
Due to rounding error in binary floating-point representation, this comparison often fails even though the math is “correct.” Use a small tolerance instead:
if (fabs(sqrt(2.0) * sqrt(2.0) - 2.0) < 1e-9) {
printf("Equal\n");
}
Best Practices
- Always link with
-lmwhen compiling with gcc or clang on Linux. - Reset
errnoto0before a math call if you plan to check it afterward, since it is never cleared automatically. - Convert degrees to radians before calling any trigonometric function.
- Never compare floating-point results with
==; use a tolerance-based comparison withfabs. - Use
isnanandisinfto validate results after operations that might overflow or hit an undefined domain. - Match function precision to your data: use
sqrtfforfloatarguments andsqrtlforlong doubleto avoid unnecessary implicit conversions. - Prefer
fmodover%when you need a remainder of floating-point values, since%only works on integer types.
Practice Exercises
- Write a program that reads a circle’s radius and prints its area (πr²) and circumference (2πr) using a defined
PIconstant andpowor plain multiplication. - Write a program that checks whether an integer is a perfect square by computing
sqrtof it, rounding the result, and verifying that the rounded value squared equals the original number. - Write a program that solves a quadratic equation
ax² + bx + c = 0using the quadratic formula andsqrt. Handle the case where the discriminant (b² - 4ac) is negative by printing a message instead of callingsqrton a negative number.
Summary
math.hdeclares numeric functions; their implementations live in libm, so link with-lmon gcc/clang.- Most functions operate on
doubleand havef/lsuffixed variants forfloatandlong double. - Trigonometric functions expect radians, not degrees — convert with
degrees * (PI / 180). - Invalid inputs set
errnotoEDOM(domain error) orERANGE(range error) and return NaN or infinity instead of stopping the program. - Never compare floating-point results with
==; use a small tolerance withfabsinstead. - Useful diagnostic functions include
isnan,isinf, andisfinitefor validating results after risky calculations.
