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++20A 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— adoublealias, shorthand forstd::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:
| Constant | Mathematical meaning | Approximate value |
|---|---|---|
e | Euler's number (base of natural log) | 2.718 281 828… |
log2e | log₂(e) | 1.442 695 040… |
log10e | log₁₀(e) | 0.434 294 481… |
pi | π | 3.141 592 653… |
inv_pi | 1/π | 0.318 309 886… |
inv_sqrtpi | 1/√π | 0.564 189 583… |
sqrt2 | √2 | 1.414 213 562… |
sqrt3 | √3 | 1.732 050 807… |
inv_sqrt3 | 1/√3 | 0.577 350 269… |
egamma | Euler–Mascheroni constant (γ) | 0.577 215 664… |
phi | Golden ratio φ = (1 + √5)/2 | 1.618 033 988… |
Syntax
#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
#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:
#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:
#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:
#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:
// 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:
#if __cpp_lib_math_constants >= 201907L
# include <numbers>
inline constexpr double kPi = std::numbers::pi;
#else
inline constexpr double kPi = 3.141592653589793238462643383;
#endifCommon 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.
float r = 2.5f;
float area = std::numbers::pi * r * r; // std::numbers::pi is double;
// result is double, silently narrowed to floatWrite 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
constexprevaluation model that makesinline constexprconstants zero-cost