Skip to content

CODATA Fundamental Physical Constants

The CODATA recommended values of the fundamental physical constants, expressed in SI units.

#include <mp-units/systems/codata.h>

using namespace mp_units;

quantity mu = measurement_of(codata::magnetic_constant).in(si::henry / si::metre);
std::cout << mu << "\n";  // 1.25664e-06 ± 2e-16 H/m

Why a separate system

The SI fixes exactly seven constants, and those live in the SI system where they belong (every adjustment namespace below aliases them, so codata::planck_constant is the very same entity as si::si2019::planck_constant). Everything CODATA publishes beyond them is measured: each value carries a standard uncertainty and shifts with every adjustment.

Keeping them here rather than in si is a deliberate cost decision. <mp-units/systems/si.h> is the header most translation units reach for, and most of them never name a CODATA constant. Making everyone pay for the full table so that a minority need not write one extra #include is the wrong trade. Include this header when you want these constants.

Generated from the source tables

The headers of this system are generated by scripts/codata_constants.py directly from the NIST "allascii" tables, which are checked into the repository verbatim. The generator accounts for every row of every table and then verifies its own output by recomputing each emitted value in the row's own unit and comparing it against the printed digits. The rationale behind generating rather than transcribing is that:

  • a measured constant transcribes the published value and the published absolute standard uncertainty digit for digit, so nothing in these headers is rounded by us,
  • values that NIST prints truncated with an ellipsis (8.617 333 262... e-5) are exact but non-terminating decimals, so they are defined by their exact symbolic relation (for example, the Boltzmann constant divided by the elementary charge) instead of by copied digits that would silently define an inexact constant while labelling it exact,
  • rows that merely restate a constant in another unit ("... in MeV", "energy equivalent", unit relationships) are not repeated, because constants are units in this library and such forms are a conversion at the point of use.

Header layout

Each adjustment ships in two headers spelling the very same constant types:

  • <mp-units/systems/codata/codataYYYY_essential.h> provides the NIST "Frequently used constants" selection (the Newtonian constant of gravitation, the fine-structure constant, particle masses, and friends),
  • <mp-units/systems/codata/codataYYYY.h> includes the essential header and adds the complete table (roughly 230 constants per adjustment).

The split is a compile-time trade. The essential tier costs nothing measurable on top of the framework headers it includes, while the complete table instantiates thousands of magnitude types and adds a few seconds per translation unit, so code that only needs G should not pay for the atomic unit of the second hyperpolarizability. <mp-units/systems/codata.h> remains the umbrella for all adjustments in full.

Two more headers hold the constants that do not belong to any single adjustment:

  • <mp-units/systems/codata/adopted_values.h> provides NIST's "adopted values": the conventional constants fixed by CGPM/CIPM resolutions rather than measured (the standard gravity, the standard atmosphere, the standard-state pressure, and the conventional Josephson and von Klitzing values). They are identical in every adjustment, which the generator verifies, so they live directly in mp_units::codata as single adjustment-invariant entities. This also makes the header the right dependency for units defined through them: yard_pound's pound_force builds on codata::standard_gravity and includes only this tiny header rather than a whole adjustment.
  • <mp-units/systems/codata/math_constants.h> defines the two solutions of the Wien displacement transcendental equations. Like π, they are exact mathematical constants, and the post-2019 Wien displacement law constants are defined through them instead of through truncated decimals.

Adjustments

Each CODATA adjustment gets its own namespace, because a measured constant has no single correct value:

quantity now = measurement_of(codata::magnetic_constant);                  // current
quantity pinned = measurement_of(codata::codata2018::magnetic_constant);   // a specific one

The most recent adjustment is inline, so the unqualified name always refers to it while older ones stay reachable by spelling them out. Moving that inline to a newer adjustment is a deliberate, documented change of the default rather than something that happens silently.

Constants that are exact under a given adjustment carry no uncertainty at all, which makes the exact-versus-measured distinction visible to the type system:

static_assert(MeasuredConstant<decltype(codata::codata2022::magnetic_constant)>);
static_assert(!MeasuredConstant<decltype(codata::codata2014::magnetic_constant)>);

The magnetic constant μ₀ (IEC 80000-6, item 6-26.1) shows why. Before 2019 the ampere was defined through μ₀, which made it exactly \(4\pi \times 10^{-7}\) H/m. The 2019 redefinition tied the ampere to the elementary charge instead, so μ₀ became measured, and its value departs from \(4\pi \times 10^{-7}\) in the tenth digit.

Not interchangeable with the hep constants

The HEP system provides codata2014/codata2018/codata2022 namespaces of its own, but those constants are not the same entities. HEP declares its own system of quantities, with energy as a base quantity and no mass dimension at all, so its constants are unrelated types that no conversion can reach.

Their published uncertainties can differ as well. Before 2019 the elementary charge was itself measured, so a mass expressed in kg and the same mass expressed in MeV/c² are separate CODATA table entries with different \(u_r\). Deriving one from the other would combine correlated uncertainties in quadrature and overstate them.

Working with the uncertainties

These constants are ordinary units, so they cost nothing until a conversion actually needs their value, and they cancel symbolically when they appear on both sides. To obtain a quantity that carries the uncertainty, use measurement_of, described in Working with Measurement Uncertainty.