std::ratio
Compile-time rational number arithmetic via template parameters — the unit system underlying std::chrono::duration.
std::ratiosince C++11A class template that represents a compile-time rational constant as a reduced fraction, encoding both numerator and denominator as intmax_t non-type template parameters.
Overview
std::ratio lives entirely at the type level. There are no runtime instances that hold a value — the rational number is the type. std::ratio<1, 3> and std::ratio<2, 6> are the same type after reduction; both expose ::num == 1 and ::den == 3 as static constexpr intmax_t members.
This design was introduced in C++11 specifically to make std::chrono::duration unit-safe at zero runtime cost. When you write std::chrono::milliseconds, its second template argument is std::milli — an alias for std::ratio<1, 1000>. Conversions between durations reduce to a ratio multiplication performed entirely by the compiler.
The header is <ratio>.
Syntax
namespace std {
template <intmax_t N, intmax_t D = 1>
class ratio {
public:
static constexpr intmax_t num; // N / gcd(N,D), with sign of N*D
static constexpr intmax_t den; // |D| / gcd(N,D)
using type = ratio<num, den>;
};
}Both template arguments must be intmax_t values. D must not be zero. The instantiation is ill-formed if the reduced numerator or denominator would overflow intmax_t.
Predefined SI prefix aliases (C++11):
using std::atto = std::ratio<1, 1'000'000'000'000'000'000LL>; // 10^-18
using std::femto = std::ratio<1, 1'000'000'000'000'000LL>; // 10^-15
using std::pico = std::ratio<1, 1'000'000'000'000LL>; // 10^-12
using std::nano = std::ratio<1, 1'000'000'000LL>; // 10^-9
using std::micro = std::ratio<1, 1'000'000LL>; // 10^-6
using std::milli = std::ratio<1, 1'000LL>; // 10^-3
using std::centi = std::ratio<1, 100LL>; // 10^-2
using std::deci = std::ratio<1, 10LL>; // 10^-1
using std::deca = std::ratio< 10, 1LL>; // 10^1
using std::hecto = std::ratio< 100, 1LL>; // 10^2
using std::kilo = std::ratio< 1'000, 1LL>; // 10^3
using std::mega = std::ratio< 1'000'000, 1LL>; // 10^6
using std::giga = std::ratio< 1'000'000'000, 1LL>; // 10^9
using std::tera = std::ratio< 1'000'000'000'000, 1LL>; // 10^12
using std::peta = std::ratio< 1'000'000'000'000'000, 1LL>; // 10^15
using std::exa = std::ratio<1'000'000'000'000'000'000, 1LL>; // 10^18atto through femto and peta/exa are optional — defined only when representable by intmax_t.
Compile-time arithmetic (C++11): Each operation produces a nested ::type that is a fully reduced std::ratio:
// ratio_add, ratio_subtract, ratio_multiply, ratio_divide
template <class R1, class R2> struct ratio_add;
template <class R1, class R2> struct ratio_subtract;
template <class R1, class R2> struct ratio_multiply;
template <class R1, class R2> struct ratio_divide;
// Comparison — each exposes ::value (bool)
template <class R1, class R2> struct ratio_equal;
template <class R1, class R2> struct ratio_not_equal;
template <class R1, class R2> struct ratio_less;
template <class R1, class R2> struct ratio_greater;
template <class R1, class R2> struct ratio_less_equal;
template <class R1, class R2> struct ratio_greater_equal;Examples
Basic ratio inspection
#include <ratio>
#include <iostream>
int main() {
using a_third = std::ratio<1, 3>;
using two_sixths = std::ratio<2, 6>; // same type after reduction
static_assert(std::ratio_equal<a_third, two_sixths>::value); // C++11
std::cout << a_third::num << '/' << a_third::den << '\n'; // 1/3
std::cout << two_sixths::num << '/' << two_sixths::den << '\n'; // 1/3
}Compile-time arithmetic
#include <ratio>
using two_sevenths = std::ratio<2, 7>;
using three_fourths = std::ratio<3, 4>;
using sum = std::ratio_add<two_sevenths, three_fourths>::type; // 29/28
using product = std::ratio_multiply<two_sevenths, three_fourths>::type; // 3/14
static_assert(sum::num == 29 && sum::den == 28);
static_assert(product::num == 3 && product::den == 14);Custom chrono durations
std::chrono::duration<Rep, Period> uses a std::ratio as its second template argument to describe how many seconds each tick represents.
#include <chrono>
#include <ratio>
#include <iostream>
// Film frame rate: one tick = 1/24 second
using fps24_duration = std::chrono::duration<unsigned long, std::ratio<1, 24>>;
// A "school lesson" in Germany: one tick = 45 minutes = 2700 seconds
using lesson_duration = std::chrono::duration<double, std::ratio<2700>>;
int main() {
fps24_duration one_second_of_film{24};
lesson_duration half_lesson{0.5};
// Convert fps24 -> milliseconds
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(one_second_of_film);
std::cout << ms.count() << " ms\n"; // 1000 ms
// lesson_duration to seconds requires explicit cast (no implicit lossy conversion)
auto secs = std::chrono::duration_cast<std::chrono::seconds>(half_lesson);
std::cout << secs.count() << " s\n"; // 1350 s
}Type-level unit arithmetic in generic code
#include <ratio>
#include <type_traits>
// A quantity type parameterised by its unit (as a ratio relative to some base)
template <typename Rep, typename Unit>
struct quantity {
Rep value;
};
// Convert between two quantity types with the same base unit
template <typename ToUnit, typename Rep, typename FromUnit>
auto unit_cast(quantity<Rep, FromUnit> q) {
// Conversion factor: FromUnit / ToUnit, computed at compile time
using factor = std::ratio_divide<FromUnit, ToUnit>;
return quantity<Rep, ToUnit>{
static_cast<Rep>(q.value * factor::num / factor::den)
};
}
using meters = std::ratio<1>;
using centimeters = std::ratio<1, 100>;
using kilometers = std::ratio<1000>;
int main() {
quantity<double, kilometers> dist{1.5};
auto in_meters = unit_cast<meters>(dist); // 1500.0
auto in_cm = unit_cast<centimeters>(dist); // 150000.0
}Best Practices
Let the library reduce for you. Never manually compute GCDs. Pass any valid N/D pair and rely on the guaranteed reduction — std::ratio<6, 9> gives you num == 2, den == 3 without effort.
Express domain units as type aliases. Naming using fps24 = std::ratio<1, 24> once and reusing it is far clearer than scattering std::ratio<1, 24> literals throughout duration declarations.
Prefer ratio_multiply/ratio_divide over manual calculation. When building compound units, compose existing ratio types rather than computing the final fraction by hand. The compiler catches overflow; you won't.
Use static_assert for ratio constraints. If a template requires a ratio smaller than std::ratio<1> (i.e., a sub-second period), encode that requirement explicitly:
template <typename Rep, typename Period>
void requires_sub_second(std::chrono::duration<Rep, Period>) {
static_assert(std::ratio_less<Period, std::ratio<1>>::value,
"Period must be finer than one second");
}Common Pitfalls
Assuming value semantics. std::ratio has no constructor and no data members. Attempting to instantiate an object and store a runtime fraction in it is a misuse — use a std::pair<intmax_t, intmax_t> or a custom rational class for runtime values.
Overflow in deeply nested arithmetic. Each arithmetic operation reduces to lowest terms, but intermediate values must still fit in intmax_t. A chain of multiplications on large ratios can produce a hard compile error (std::ratio is required to be ill-formed on overflow). Factor your ratios before composing.
Misreading ::num and ::den on negative ratios. The sign is always canonicalized onto ::num; ::den is always positive. std::ratio<-3, -5> gives num == 3, den == 5. std::ratio<3, -5> gives num == -3, den == 5.
Implicit duration conversions that lose precision. std::chrono deliberately blocks implicit conversion from a coarser to a finer duration type when Rep is integral. The underlying check uses ratio comparison. If you get a compilation error converting between durations, use duration_cast and understand you are accepting truncation.
See Also
std::chrono::duration— the primary consumer ofstd::ratio; itsPeriodtemplate argument is astd::ratiospecifying seconds per tickstd::chrono::time_point— pairs a clock with aduration, itself parameterised on astd::ratio<type_traits>— the broader metaprogramming vocabulary that ratio arithmetic complements