Skip to content
C++
Library
since C++20
Basic

std::numbers — Mathematical Constants

C++20 compile-time mathematical constants (π, e, φ, √2, and more) in <numbers>, available as double aliases and as precision-preserving variable templates.

std::numberssince C++20

A C++20 namespace that exposes eleven mathematical constants — including π, e, φ, √2, and the Euler–Mascheroni constant — as inline constexpr values declared in <numbers>, each available both as a double alias and as a floating-point variable template.

Overview

Before C++20, portable access to mathematical constants was genuinely awkward. M_PI is a POSIX extension absent from the C++ standard; MSVC omits it unless _USE_MATH_DEFINES is defined, and relying on it in portable code invites subtle build failures. The alternative — scattering literals like 3.14159265358979323846 through the codebase — is error-prone and hard to audit.

C++20 standardised eleven constants in the std::numbers namespace inside <numbers>. Every constant follows a two-name convention:

  • std::numbers::X — a double alias, shorthand for std::numbers::X_v<double>.
  • std::numbers::X_v<T> — a variable template constrained to floating-point types (float, double, long double, or extended floating-point types). This is the canonical form.

Both forms are inline constexpr, fully evaluated at compile time with zero runtime cost. The variable template X_v<T> is constrained to std::floating_point<T> (a C++20 concept); passing an integral type is a hard compile error.

The complete set introduced in C++20:

ConstantMathematical meaningApproximate value
eEuler's number (base of natural log)2.718 281 828…
log2elog₂(e)1.442 695 040…
log10elog₁₀(e)0.434 294 481…
piπ3.141 592 653…
inv_pi1/π0.318 309 886…
inv_sqrtpi1/√π0.564 189 583…
sqrt2√21.414 213 562…
sqrt3√31.732 050 807…
inv_sqrt31/√30.577 350 269…
egammaEuler–Mascheroni constant (γ)0.577 215 664…
phiGolden ratio φ = (1 + √5)/21.618 033 988…

Syntax

cpp
#include <numbers>     // C++20

// Double-precision aliases
double a = std::numbers::pi;       // 3.14159265358979...
double b = std::numbers::e;        // 2.71828182845904...
double c = std::numbers::sqrt2;    // 1.41421356237309...
double d = std::numbers::phi;      // 1.61803398874989...
double g = std::numbers::egamma;   // 0.57721566490153...

// Variable templates — match precision to the computation type
float       pi_f  = std::numbers::pi_v<float>;
double      pi_d  = std::numbers::pi_v<double>;
long double pi_ld = std::numbers::pi_v<long double>;

Examples

Angle conversion and trigonometry

cpp
#include <cmath>
#include <numbers>
#include <print>      // C++23

constexpr double to_radians(double degrees) noexcept {
    return degrees * (std::numbers::pi / 180.0);
}

constexpr double to_degrees(double radians) noexcept {
    return radians * (180.0 / std::numbers::pi);
}

int main() {
    // Angle of elevation: 100ft high steeple, 50ft away
    double angle_rad = std::atan2(100.0, 50.0);
    std::println("Steeple angle: {:.4f}°", to_degrees(angle_rad));  // 63.4349°

    // Verify cos(45°) via the conversion
    std::println("cos(45°) = {:.6f}", std::cos(to_radians(45.0)));  // 0.707107
}

Precision-preserving generic code

The bare pi alias is double. In a function templated on T, using pi silently widens float operands to double and narrows the result back — a precision round-trip the caller did not ask for. Use pi_v<T> instead:

cpp
#include <cmath>
#include <concepts>    // C++20
#include <numbers>

template <std::floating_point T>    // C++20 concept
constexpr T circle_area(T radius) noexcept {
    return std::numbers::pi_v<T> * radius * radius;
}

template <std::floating_point T>
constexpr T sphere_volume(T radius) noexcept {
    return (T{4} / T{3}) * std::numbers::pi_v<T> * radius * radius * radius;
}

// Both functions are usable at compile time
static_assert(circle_area(1.0f)  > 3.14f);
static_assert(sphere_volume(1.0) > 4.18);

// float computation stays entirely in float — no implicit widening
float pond_area = 78.5f;
float diameter  = 2.0f * std::sqrt(pond_area / std::numbers::pi_v<float>);

The static_assert calls verify both instantiations at compile time; because the arguments are constexpr, the entire computation is constant-folded.

Efficient logarithm base change

log2e and log10e are multiplication-ready reciprocals of common logarithm bases. They replace a runtime division by std::log(2) or std::log(10) with a compile-time constant multiply:

cpp
#include <cmath>
#include <numbers>

// log₂(x) = ln(x) * log₂(e)   — constant multiply, no division
double fast_log2(double x) noexcept {
    return std::log(x) * std::numbers::log2e;
}

// log₁₀(x) = ln(x) * log₁₀(e)
double fast_log10(double x) noexcept {
    return std::log(x) * std::numbers::log10e;
}

This is numerically equivalent to dividing by the respective log, but the compiler folds the constant multiplication into a single FP instruction.

Euler–Mascheroni constant and harmonic series

γ appears in asymptotic analysis of harmonic numbers: H(n) = 1 + 1/2 + … + 1/n satisfies H(n) ≈ ln(n) + γ + 1/(2n). This approximation is tight even for modest n:

cpp
#include <cmath>
#include <numbers>
#include <print>    // C++23

double harmonic_approx(int n) noexcept {
    double nd = static_cast<double>(n);
    return std::log(nd) + std::numbers::egamma + 1.0 / (2.0 * nd);
}

int main() {
    // Exact H(10) = 7381/2520 ≈ 2.928968
    std::println("H(10)  approx: {:.6f}", harmonic_approx(10));   // 2.929177
    std::println("H(100) approx: {:.6f}", harmonic_approx(100));  // 5.187378
}

Best Practices

Always use pi_v<T> in templates. std::numbers::pi is double. Inside a function templated on a floating-point type T, using the bare alias forces implicit conversion; pi_v<T> keeps arithmetic in the caller's chosen type at no cost.

Prefer these over M_PI unconditionally. M_PI requires non-portable preprocessor guards and is absent by default on MSVC. std::numbers::pi is always available under C++20 with no configuration required.

Define composite constants once, not inline. If your code repeatedly needs 2π or 180/π, define them in a single header rather than computing them at every call site:

cpp
// Portable, ODR-safe, zero runtime cost
inline constexpr double two_pi      = 2.0 * std::numbers::pi;    // C++20
inline constexpr double half_pi     = std::numbers::pi / 2.0;
inline constexpr double rad_per_deg = std::numbers::pi / 180.0;
inline constexpr double deg_per_rad = 180.0 / std::numbers::pi;

Guard C++17 fall-through with feature-test macros. Code that must remain compatible with C++17 can test for the feature before using it:

cpp
#if __cpp_lib_math_constants >= 201907L
#  include <numbers>
   inline constexpr double kPi = std::numbers::pi;
#else
   inline constexpr double kPi = 3.141592653589793238462643383;
#endif

Common Pitfalls

Wrong header. <cmath> does not pull in <numbers>. Referencing std::numbers::pi without #include <numbers> is a compile error.

Implicit narrowing via the double alias.

cpp
float r    = 2.5f;
float area = std::numbers::pi * r * r;  // std::numbers::pi is double;
                                         // result is double, silently narrowed to float

Write std::numbers::pi_v<float> to keep the entire computation in float.

Redefining constants as macros. Writing #define PI std::numbers::pi defeats namespacing, breaks in contexts where std:: is not visible, and prevents the compiler from diagnosing type mismatches. Use the namespace name directly.

Using inv_sqrt3 when you want the wrong thing. std::numbers::inv_sqrt3 equals 1/√3 ≈ 0.5774. It does not equal 1/3 ≈ 0.3333. The name is compact but the meaning requires a moment's thought; if the computation isn't obvious at the call site, bind to a named local variable.

See Also

  • <cmath> — runtime floating-point functions (std::sin, std::log, std::sqrt) that consume these constants
  • <complex> — complex arithmetic operating on the same floating-point types
  • <numeric>std::midpoint, std::lerp, and other numeric utilities (C++20)
  • Constant expressions — the constexpr evaluation model that makes inline constexpr constants zero-cost