Skip to content
C++
Library
since C++11
Intermediate

std::ratio

Compile-time rational number arithmetic via template parameters — the unit system underlying std::chrono::duration.

std::ratiosince C++11

A 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

cpp
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):

cpp
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^18

atto 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:

cpp
// 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

cpp
#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

cpp
#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.

cpp
#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

cpp
#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:

cpp
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 of std::ratio; its Period template argument is a std::ratio specifying seconds per tick
  • std::chrono::time_point — pairs a clock with a duration, itself parameterised on a std::ratio
  • <type_traits> — the broader metaprogramming vocabulary that ratio arithmetic complements