<cmath>
Standard C++ header providing mathematical functions for floating-point arithmetic, rounding, classification, and error handling.
<cmath>since C++98The <cmath> header provides the C++ standard mathematical function library β a superset of C's <math.h> β placing all names in namespace std and adding overloads for float, double, and long double argument types.
Overview
<cmath> is C++'s primary header for floating-point mathematics. It wraps and extends the C <math.h> library, organising its contents into several functional groups:
- Basic:
abs,fabs,fmod,remainder,remquo,fma,fmax,fmin,fdim - Exponential / logarithmic:
exp,exp2,expm1,log,log2,log10,log1p - Power / root:
pow,sqrt,cbrt,hypot - Trigonometric:
sin,cos,tan,asin,acos,atan,atan2 - Hyperbolic:
sinh,cosh,tanh,asinh,acosh,atanh - Rounding:
ceil,floor,trunc,round,lround,llround,nearbyint,rint,lrint,llrint - Floating-point manipulation:
frexp,ldexp,modf,scalbn,scalbln,ilogb,logb,nextafter,nexttoward,copysign - Classification / comparison:
fpclassify,isfinite,isinf,isnan,isnormal,signbit,isgreater,isless,isunordered
Prior to C++11 the header provided only double overloads; C++11 added float and long double overloads for every function, plus the classification macros were promoted to proper function templates. Several functions gained constexpr qualification in C++20 and C++23 (see individual notes below).
Relationship to <math.h>
Including <cmath> is preferred over <math.h> in C++ code. Both ultimately expose the same functions, but <cmath> guarantees placement in namespace std, eliminating naming collisions and enabling argument-dependent lookup. The <math.h> form additionally injects names into the global namespace β a behaviour that is implementation-defined for <cmath> and not to be relied upon.
Syntax
#include <cmath>
// All functions live in namespace std
double r = std::sqrt(2.0);
float s = std::sqrtf(2.0f); // C-style suffix form β still valid
float t = std::sqrt(2.0f); // C++11: preferred float overloadSince C++11 every function is overloaded for float, double, and long double. When an integer argument is passed the result type is double:
double x = std::sqrt(4); // C++11: int promoted to double β 2.0Examples
Geometry: distance and angle
#include <cmath>
#include <numbers> // C++20: std::numbers::pi
#include <print> // C++23: std::println
struct Vec2 { double x, y; };
double magnitude(Vec2 v) {
return std::hypot(v.x, v.y); // avoids intermediate overflow
}
double angle_deg(Vec2 v) {
double rad = std::atan2(v.y, v.x);
return rad * (180.0 / std::numbers::pi); // C++20
}
int main() {
Vec2 v{3.0, 4.0};
std::println("magnitude: {}", magnitude(v)); // 5.0
std::println("angle: {}Β°", angle_deg(v)); // 53.13...
}std::hypot computes β(xΒ²+yΒ²) without the overflow risk of writing std::sqrt(x*x + y*y). Since C++17, a three-argument form std::hypot(x, y, z) computes the 3-D Euclidean norm.
Floating-point classification
#include <cmath>
#include <limits>
bool safe_log(double x, double& result) {
if (!std::isfinite(x) || x <= 0.0)
return false;
result = std::log(x);
return true;
}
void inspect(double v) {
switch (std::fpclassify(v)) {
case FP_INFINITE: /* handle inf */ break;
case FP_NAN: /* handle NaN */ break;
case FP_SUBNORMAL: /* handle denormal */ break;
case FP_ZERO: /* handle zero */ break;
case FP_NORMAL: /* ordinary value */ break;
}
}std::isnan, std::isinf, and std::isfinite (C++11 function templates) replace the isnan / isinf macros from C. Prefer the std:: versions; the global-namespace macros may not exist in all translation units.
Rounding modes
#include <cmath>
// C++11: all rounding variants exist
double v = 2.7;
std::floor(v); // 2.0 β toward -β
std::ceil(v); // 3.0 β toward +β
std::trunc(v); // 2.0 β toward zero (C++11)
std::round(v); // 3.0 β nearest, halfway away from zero
std::nearbyint(v); // 3.0 β nearest, respects current rounding mode, no FE_INEXACT
std::rint(v); // 3.0 β nearest, respects current rounding mode, raises FE_INEXACTstd::nearbyint and std::rint differ only in whether they raise the FE_INEXACT floating-point exception. If you need to suppress that exception (e.g., in hot inner loops where <cfenv> error tracking is active), prefer std::nearbyint.
Fused multiply-add
#include <cmath>
// C++11: std::fma computes (x * y) + z with a single rounding step
double dot(double ax, double bx, double ay, double by) {
return std::fma(ax, bx, ay * by);
}std::fma (C++11) is exact to within one ULP when the hardware provides a native FMA instruction. On platforms without native FMA it is emulated but still produces the correct mathematical result. Use it whenever intermediate rounding in x*y + z would be a problem β polynomial evaluation being the classic case.
Error handling
#include <cmath>
#include <cerrno>
#include <cstring>
double safe_sqrt(double x) {
errno = 0;
double r = std::sqrt(x);
if (errno == EDOM)
throw std::domain_error("sqrt of negative");
return r;
}Error reporting from <cmath> is controlled by math_errhandling (defined in <cmath>, not in namespace std). It holds a bitwise OR of:
| Constant | Meaning |
|---|---|
MATH_ERRNO | Errors reported via errno |
MATH_ERREXCEPT | Errors raised as floating-point exceptions (<cfenv>) |
At least one of the two bits is set by any conforming implementation. On most Linux/glibc targets both are set; on MSVC only MATH_ERRNO is. Do not assume a specific combination.
Best Practices
Prefer std::hypot over manual Pythagorean sums. Writing std::sqrt(x*x + y*y) overflows when x or y exceeds βDBL_MAX β 1.3Γ10ΒΉβ΅β΄. std::hypot is required by the standard to avoid spurious overflow and underflow.
Use std::fma for compensated arithmetic. When building polynomial evaluators (Horner's method) or compensated summations, std::fma eliminates the intermediate rounding step and typically maps to a single hardware instruction on x86-64 (VFMADD), ARM (FMADD), and POWER (fmadd).
Classify before computing. Always guard std::log, std::sqrt, std::asin, and similar domain-restricted functions with std::isfinite and range checks on untrusted input. Passing a negative value to std::sqrt sets errno = EDOM and returns NaN β it does not throw.
Match argument and return types. Mixing float arguments with a double result variable triggers an implicit widening conversion that silently defeats the purpose of using float for performance. Assign to the same type: float r = std::sqrtf(x) or float r = std::sqrt(x) (C++11 overload).
Do not use using namespace std; to shorten math calls in headers. Importing the entire std namespace in a header drags in hundreds of names and is a well-known source of ODR violations.
Common Pitfalls
std::abs vs std::fabs for floating-point. std::abs with a double argument is declared in <cmath> since C++11. Before C++11, std::abs for floating-point was only guaranteed by including <cstdlib> (which provided the integer version) or <cmath>. With C++11 or later, prefer std::abs with <cmath> included. Including only <cstdlib> and calling std::abs(3.7) silently truncates to integer abs on older compilers.
NaN propagation is asymmetric for min/max. std::fmin(NaN, 1.0) returns 1.0 (NaN is treated as missing data per IEEE 754), whereas std::min(NaN, 1.0) has unspecified behaviour and frequently returns the wrong value. Use std::fmin/std::fmax when one operand may be NaN.
std::pow(base, 2) is not an optimisation. Many compilers do not special-case integer exponents in std::pow; the call is typically slower and less accurate than base * base. Write the multiplication explicitly or use std::fma(base, base, 0.0).
math_errhandling is not in namespace std. Unlike nearly everything else in <cmath>, math_errhandling, MATH_ERRNO, and MATH_ERREXCEPT are macros defined at global scope. Do not write std::math_errhandling.
Integer literals produce double. std::sqrt(2) is valid C++11 (integer argument overload resolves to double), but std::sqrt(2.f) selects the float overload. Ensure literal suffixes match the intended precision.
Fused Multiply-Add: std::fma
std::fma(x, y, z) (C++11) computes (x * y) + z as if with infinite intermediate precision, then rounds the final result once. Ordinary x * y + z incurs two rounding steps β one after the multiplication and one after the addition β so the two expressions can give different answers.
#include <cmath>
#include <cfloat>
// naive: two roundings, up to 2 ULP error
double naive = x * y + z;
// fma: one rounding, at most 0.5 ULP error (correctly rounded)
double exact = std::fma(x, y, z);Hardware mapping
When the compiler can prove that FP_FAST_FMA (or FP_FAST_FMAF / FP_FAST_FMAL) is defined, std::fma maps to a single native instruction:
| Architecture | Instruction | Availability |
|---|---|---|
| x86-64 FMA3 | VFMADD132SD / VFMADD213SD | Intel Haswell (2013) and later, AMD Piledriver (2012) and later |
| x86-64 FMA4 | VFMADDSD | AMD Bulldozer (2011) only |
| ARM / AArch64 | FMADD / VFMA | ARMv8-A and Cortex-M4F with VFPv4 |
| POWER | fmadd | All modern POWER cores |
When no native FMA is available, the implementation falls back to a software emulation that still produces the correctly rounded result but is significantly slower. Pass -mfma (GCC/Clang) or /arch:AVX2 (MSVC) to unlock hardware FMA on x86.
Check at compile time whether fast FMA is available:
#include <cmath>
#ifdef FP_FAST_FMA
double r = std::fma(a, b, c); // single instruction
#else
double r = a * b + c; // fast but two-rounded; or accept slower std::fma
#endifWhen std::fma matters most
Horner's method (polynomial evaluation). Each step of Horner's scheme is a multiply-add, so using std::fma halves the number of rounding errors:
// Evaluate p(x) = c0 + c1*x + c2*x^2 + c3*x^3
// Horner form: ((c3*x + c2)*x + c1)*x + c0
double horner(double x, double c0, double c1, double c2, double c3) {
double r = c3;
r = std::fma(r, x, c2);
r = std::fma(r, x, c1);
r = std::fma(r, x, c0);
return r;
}Dot products. Accumulating a[i] * b[i] with std::fma keeps each product exact before adding it to the running sum:
double dot(const double* a, const double* b, std::size_t n) {
double sum = 0.0;
for (std::size_t i = 0; i < n; ++i)
sum = std::fma(a[i], b[i], sum);
return sum;
}Kahan summation alternative. The classic Kahan compensated-sum algorithm requires two additions per element. With FMA, a single std::fma can replace the correction step and achieve similar accuracy at lower cost.
Exact difference of products. The expression a*b - c*d is notoriously ill-conditioned when the two products nearly cancel. Using two FMA calls gives the exact result:
// Exactly computes a*b - c*d without intermediate cancellation error
double diff_of_products(double a, double b, double c, double d) {
double w = c * d;
double err = std::fma(-c, d, w); // exact rounding error of c*d
double r = std::fma(a, b, -w);
return r + err;
}Overflow-Safe Norms: std::hypot
The problem with the naive formula
Computing sqrt(x*x + y*y) directly overflows when |x| or |y| exceeds roughly sqrt(DBL_MAX) β 1.34Γ10ΒΉβ΅β΄, and underflows to zero when both are subnormal. std::hypot is required by the standard to produce a correctly rounded result without spurious overflow or underflow.
#include <cmath>
#include <limits>
double big = std::numeric_limits<double>::max() / 2.0;
// Naive overflows to inf even though the true answer is finite:
double bad = std::sqrt(big * big + big * big); // +inf
// std::hypot handles it correctly:
double good = std::hypot(big, big); // 1.27...e+307Signatures
// Two-argument form (C++11)
double std::hypot(double x, double y);
float std::hypot(float x, float y);
long double std::hypot(long double x, long double y);
// Three-argument form (C++17): sqrt(x^2 + y^2 + z^2)
double std::hypot(double x, double y, double z);
float std::hypot(float x, float y, float z);
long double std::hypot(long double x, long double y, long double z);Internal algorithm
A typical implementation scales all inputs by the maximum of |x| and |y| (or |z|), computes the norm, then scales back. This keeps the squared terms inside the representable range:
// Conceptual implementation (simplified)
// The standard library version handles all IEEE 754 edge cases.
double safe_hypot2(double x, double y) {
x = std::fabs(x);
y = std::fabs(y);
if (x < y) std::swap(x, y);
if (x == 0.0) return 0.0;
double t = y / x;
return x * std::sqrt(std::fma(t, t, 1.0));
}std::fma in the inner sqrt call eliminates a rounding step and, combined with the scaling, keeps the result within 1 ULP of the true value on conforming implementations.
Practical use
#include <cmath>
#include <array>
// 3-D distance between two points (C++17)
struct Vec3 { double x, y, z; };
double distance(Vec3 a, Vec3 b) {
return std::hypot(a.x - b.x, a.y - b.y, a.z - b.z);
}
// Magnitude of a complex number without <complex>
double complex_abs(double re, double im) {
return std::hypot(re, im);
}Error Handling in Depth
math_errhandling, MATH_ERRNO, and MATH_ERREXCEPT
Every <cmath> function must report errors through at least one of two channels, selected by the implementation-defined macro math_errhandling:
// math_errhandling is a macro, NOT in namespace std
// It holds a bitwise OR of:
// MATH_ERRNO (1) β errors set errno
// MATH_ERREXCEPT (2) β errors raise IEEE 754 floating-point exceptions
if (math_errhandling & MATH_ERRNO) {
// errors are reported via errno
}
if (math_errhandling & MATH_ERREXCEPT) {
// errors raise FE_INVALID, FE_DIVBYZERO, or FE_OVERFLOW
}| Platform | Typical value |
|---|---|
| Linux / glibc | `MATH_ERRNO |
| macOS / libm | `MATH_ERRNO |
| MSVC / Windows | MATH_ERRNO only |
| Embedded (newlib) | Varies; often MATH_ERRNO only |
Never hard-code which channel to use. Check math_errhandling at runtime.
Checking floating-point exception flags with std::fetestexcept
When math_errhandling & MATH_ERREXCEPT is non-zero, you can check exception flags from <cfenv> instead of (or alongside) errno:
#include <cmath>
#include <cfenv>
#pragma STDC FENV_ACCESS ON // required pragma: tell the compiler not to
// reorder or optimise away fenv accesses
double checked_log(double x) {
std::feclearexcept(FE_ALL_EXCEPT); // clear all flags first
double result = std::log(x);
if (std::fetestexcept(FE_INVALID)) {
// x was NaN β result is NaN
}
if (std::fetestexcept(FE_DIVBYZERO)) {
// x was exactly 0.0 β result is -inf (pole error)
}
if (std::fetestexcept(FE_OVERFLOW)) {
// result too large for double
}
return result;
}The five standard exception flags are:
| Flag | Raised when |
|---|---|
FE_INVALID | Operation on NaN or invalid domain (sqrt(-1)) |
FE_DIVBYZERO | Exact infinity result from finite inputs (log(0)) |
FE_OVERFLOW | Result magnitude exceeds DBL_MAX |
FE_UNDERFLOW | Result magnitude is a nonzero subnormal |
FE_INEXACT | Result is not exactly representable |
The #pragma STDC FENV_ACCESS ON directive is mandatory to prevent the compiler from eliding or reordering floating-point operations that would otherwise make the flags unobservable.
NaN Propagation and IEEE 754 Classification
Quiet NaN propagation rules
Under IEEE 754, any arithmetic operation that receives a quiet NaN (qNaN) as an input produces a quiet NaN as its output. This "NaN poison" property means that errors propagate silently through long computations and can be detected with a single std::isnan check at the end:
#include <cmath>
#include <limits>
double x = std::numeric_limits<double>::quiet_NaN();
// All of these produce NaN β the "poison" propagates:
double a = x + 1.0; // NaN
double b = std::sqrt(x); // NaN
double c = std::fma(x, 2.0, 3.0); // NaN
double d = std::sin(x); // NaN
// But comparison operators always return false with NaN:
bool eq = (x == x); // false β the classic NaN self-inequality test
bool ne = (x != x); // true β reliable NaN check without <cmath>
// Prefer the standard predicate:
bool is_bad = std::isnan(x); // trueImportant exceptions to NaN propagation:
std::fmax(NaN, 1.0)returns1.0(treats NaN as missing data, per IEEE 754-2008).std::fmin(NaN, 1.0)returns1.0for the same reason.std::isunordered(x, y)returnstrueif either argument is NaN; comparison predicates (std::isgreater, etc.) never raiseFE_INVALIDfor NaN inputs.
Signaling NaN
A signaling NaN (sNaN) raises FE_INVALID immediately on any arithmetic use. They are rarely produced by computations but can be constructed via std::numeric_limits<T>::signaling_NaN(). Their primary use is initialising memory to a trap value during debugging: any accidental read of an uninitialised floating-point variable immediately raises an exception.
#include <limits>
float trap = std::numeric_limits<float>::signaling_NaN();
// Reading 'trap' in arithmetic raises FE_INVALID on platforms
// that support signaling NaN (most x86 and ARM systems do).Full classification with std::fpclassify
#include <cmath>
void describe(double v) {
switch (std::fpclassify(v)) {
case FP_NAN: // either quiet or signaling NaN
case FP_INFINITE: // +inf or -inf; use std::signbit to distinguish
case FP_ZERO: // +0.0 or -0.0; std::signbit(v) for signed zero
case FP_SUBNORMAL: // |v| < DBL_MIN (gradual underflow)
case FP_NORMAL: // ordinary representable value
}
}The four predicates std::isnan, std::isinf, std::isfinite, and std::isnormal are typically faster than std::fpclassify because they compile to a single comparison or bit-test instruction on modern targets. Use std::fpclassify only when you need to distinguish subnormals or signed zeros.
C++17 Mathematical Special Functions
C++17 added 17 families of mathematical special functions directly to <cmath>. They were previously available only through third-party libraries (Boost.Math, GSL). All follow the naming convention of the underlying mathematical function; the f and l suffix forms select float and long double, respectively.
#include <cmath> // special functions are in <cmath> from C++17
// All signatures follow the pattern:
// double func_name(args...);
// float func_namef(args...);
// long double func_namel(args...);Orthogonal polynomials
These appear throughout physics as solutions to differential equations arising from separation of variables in various coordinate systems.
// Laguerre polynomials L_n(x) β quantum harmonic oscillator,
// hydrogen atom radial wave functions
double std::laguerre(unsigned n, double x);
// Associated Laguerre polynomials L_n^m(x) β hydrogen atom
// wave functions with orbital angular momentum quantum number m
double std::assoc_laguerre(unsigned n, unsigned m, double x);
// Legendre polynomials P_l(x) β solutions on [-1, 1],
// gravitational/electrostatic multipole expansions, x in [-1,1]
double std::legendre(unsigned l, double x);
// Associated Legendre functions P_l^m(x) β building block
// for spherical harmonics
double std::assoc_legendre(unsigned l, unsigned m, double x);
// Hermite polynomials H_n(x) β quantum harmonic oscillator
// energy eigenstates, probabilists' form
double std::hermite(unsigned n, double x);
// Spherical harmonics angular part Y_l^m(theta, phi=0):
// sph_legendre gives the theta-dependent factor
double std::sph_legendre(unsigned l, unsigned m, double theta);Bessel functions
Bessel functions are solutions to Bessel's differential equation. They model wave propagation in cylindrical and spherical coordinates, heat conduction in cylinders, and signal processing filter design.
// Bessel functions of the first kind J_nu(x):
// bounded at the origin; oscillatory for large x
double std::cyl_bessel_j(double nu, double x);
// Neumann functions (Bessel functions of the second kind) Y_nu(x):
// diverge at the origin; oscillatory for large x
double std::cyl_neumann(double nu, double x);
// Modified Bessel functions of the first kind I_nu(x):
// exponentially growing; arise in probability (Rice distribution)
double std::cyl_bessel_i(double nu, double x);
// Modified Bessel functions of the second kind K_nu(x):
// exponentially decaying; arise in heat transfer, elasticity
double std::cyl_bessel_k(double nu, double x);
// Spherical Bessel functions of the first kind j_n(x):
// appear in quantum scattering, acoustic resonators
double std::sph_bessel(unsigned n, double x);
// Spherical Neumann functions n_n(x)
double std::sph_neumann(unsigned n, double x);Elliptic integrals
Elliptic integrals arise in computing arc lengths of ellipses, pendulum periods for large amplitudes, and inductance of coils.
// Complete elliptic integral of the first kind K(k):
// period of a nonlinear pendulum, k is the modulus in [0,1)
double std::comp_ellint_1(double k);
// Complete elliptic integral of the second kind E(k):
// arc length of an ellipse with eccentricity k
double std::comp_ellint_2(double k);
// Complete elliptic integral of the third kind Pi(nu, k)
double std::comp_ellint_3(double k, double nu);
// Incomplete forms with upper limit phi:
double std::ellint_1(double k, double phi);
double std::ellint_2(double k, double phi);
double std::ellint_3(double k, double nu, double phi);Other special functions
// Beta function B(x, y) = Gamma(x)*Gamma(y)/Gamma(x+y):
// normalisation constant for the beta distribution,
// combinatorics, Bayesian statistics
double std::beta(double x, double y);
// Exponential integral Ei(x) = -P.V. integral from -x to inf of e^{-t}/t dt:
// radiative transfer, plasma physics
double std::expint(double x);
// Riemann zeta function zeta(s):
// analytic number theory, quantum field theory, statistical mechanics
double std::riemann_zeta(double s);Quick example β hydrogen s-orbital probability density
#include <cmath>
#include <numbers> // C++20
// Radial probability density |R_10(r)|^2 for hydrogen 1s orbital
// using the associated Laguerre polynomial (n=1, l=0, so L_0^1)
double hydrogen_1s_radial_density(double r_over_a0) {
double rho = 2.0 * r_over_a0; // dimensionless radius
double L = std::assoc_laguerre(0, 1, rho); // L_0^1(rho) = 1
double R = 2.0 * std::exp(-r_over_a0) * L;
return R * R; // |R|^2 is the probability density
}C++20 Utilities: std::lerp and std::midpoint
std::lerp β linear interpolation
std::lerp(a, b, t) (defined in <cmath> since C++20) returns a + t*(b-a) with the following guarantees that the naive formula cannot provide:
- Exactness at boundaries:
std::lerp(a, b, 0.0) == aandstd::lerp(a, b, 1.0) == bare guaranteed (no rounding surprise at the endpoints). - Monotonicity: if
t1 <= t2thenstd::lerp(a, b, t1) <= std::lerp(a, b, t2)whena <= b. - Consistency:
std::lerp(a, b, t)fortoutside[0, 1]gives a well-defined extrapolated value without undefined behaviour.
#include <cmath> // C++20: std::lerp
float blend_colour(float from, float to, float t) {
return std::lerp(from, to, t); // guaranteed exact at t=0 and t=1
}
// Why the naive formula breaks:
float a = 0.1f, b = 0.2f;
float t = 1.0f;
float naive = a + t * (b - a); // may not equal b exactly due to
// floating-point subtraction error
float safe = std::lerp(a, b, t); // == b exactlyThe naive formula a + t*(b-a) fails at t=1 because b - a is not exactly representable in binary floating-point when a and b differ in magnitude. The standard library implementation uses a branch to return b directly when t == 1.0, and switches between two algebraic forms for the general case to preserve monotonicity.
std::midpoint β overflow-safe average
std::midpoint(a, b) (defined in <numeric> since C++20) computes the exact midpoint of two values without the intermediate overflow that afflicts (a + b) / 2:
#include <numeric> // C++20: std::midpoint
// Classic bug in binary search:
int mid_bad = (lo + hi) / 2; // overflows for large lo and hi
int mid_good = std::midpoint(lo, hi); // never overflows
// Works for floating-point too:
double fmid = std::midpoint(1e308, -1e308); // 0.0, not NaNFor integers, std::midpoint always rounds toward the first argument, making it deterministic. For pointers, both arguments must point into the same array; the result is the pointer to the middle element. For floating-point, std::midpoint(a, b) is guaranteed to be at most 1 ULP from the true mathematical midpoint (a + b) / 2.
// Binary search using std::midpoint (C++20):
#include <algorithm>
#include <numeric>
#include <span>
int binary_search_safe(std::span<const int> arr, int target) {
int lo = 0, hi = static_cast<int>(arr.size()) - 1;
while (lo <= hi) {
int mid = std::midpoint(lo, hi);
if (arr[mid] == target) return mid;
if (arr[mid] < target) lo = mid + 1;
else hi = mid - 1;
}
return -1;
}See Also
<numbers>β C++20 mathematical constants (std::numbers::pi,std::numbers::e, β¦)<cfenv>β floating-point environment control (rounding mode, exception flags)<complex>β complex-number overloads of most<cmath>functions<numeric>βstd::gcd,std::lcm,std::midpoint,std::lerp(C++17/20)<valarray>β element-wise application of math functions to arrays