Boost C++ Libraries Home Libraries People FAQ More

PrevUpHomeNext

User's Guide

Getting Started
Tutorial
Example
External Resources
Getting Boost.Ratio

Boost.Ratio is part of Boost, so you get it as part of the Boost release.

Building Boost.Ratio

Boost.Ratio is a header only library, so no need to compile anything, you just need to include <boost/ratio.hpp>.

Building an executable that uses Boost.Ratio

No link is needed.

Exception safety

All functions in the library are exception-neutral, providing the strong exception safety guarantee.

Thread safety

All functions in the library are thread-unsafe except when noted explicitly.

Supported compilers

Boost.Ratio should work with any C++11 conforming compiler.

Ratio

ratio is a general purpose utility inspired by Walter Brown allowing one to easily and safely compute rational values at compile-time. The ratio class catches all errors (such as divide by zero and overflow) at compile time. It is used in the duration and time_point classes to efficiently create units of time. It can also be used in other "quantity" libraries or anywhere there is a rational constant which is known at compile-time. The use of this utility can greatly reduce the chances of run-time overflow because the ratio (and any ratios resulting from ratio arithmetic) are always reduced to the lowest terms.

ratio is a template taking two intmax_ts, with the second defaulted to 1. In addition to copy constructors and assignment, it only has two public members, both of which are static const intmax_t. One is the numerator of the ratio and the other is the denominator. The ratio is always normalized such that it is expressed in lowest terms, and the denominator is always positive. When the numerator is 0, the denominator is always 1.

Example:

typedef ratio<5, 3>   five_thirds;
// five_thirds::num == 5, five_thirds::den == 3

typedef ratio<25, 15> also_five_thirds;
// also_five_thirds::num == 5, also_five_thirds::den == 3

typedef ratio_divide<five_thirds, also_five_thirds>::type one;
// one::num == 1, one::den == 1

This facility also includes convenience typedefs for the SI prefixes atto through exa corresponding to their internationally recognized definitions (in terms of ratio). This is a tremendous syntactic convenience. It will prevent errors in specifying constants as one no longer has to double count the number of zeros when trying to write millions or billions.

Example:

typedef ratio_multiply<ratio<5>, giga>::type _5giga;
// _5giga::num == 5000000000, _5giga::den == 1

typedef ratio_multiply<ratio<5>, nano>::type _5nano;
// _5nano::num == 1, _5nano::den == 200000000
Ratio I/O

For each ratio<N, D> there exists a ratio_string<ratio<N, D>, CharT> for which you can query two strings: symbol and prefix. For those ratio's that correspond to an SI prefix prefix corresponds to the internationally recognized prefix, stored as a basic_string<CharT>. For example ratio_string<mega, char>::prefix() returns string("mega"). For those ratios that correspond to an SI prefix symbol corresponds to the internationally recognized symbol, stored as a basic_string<CharT>. For example, ratio_string<mega, char>::symbol() returns string("M"). For all other ratios, both prefix() and symbol() return a basic_string containing "[ratio::num/ratio::den]".

ratio_string<ratio<N, D>, CharT> is only defined for four character types:

  • char: UTF-8
  • char16_t: UTF-16
  • char32_t: UTF-32
  • wchar_t: UTF-16 (if wchar_t is 16 bits) or UTF-32

When the character is char, UTF-8 will be used to encode the names. When the character is char16_t, UTF-16 will be used to encode the names. When the character is char32_t, UTF-32 will be used to encode the names. When the character is wchar_t, the encoding will be UTF-16 if wchar_t is 16 bits, and otherwise UTF-32.

The symbol (Greek mu or μ) for micro is defined by Unicode to be U+00B5.

Examples:

#include <boost/ratio/ratio_io.hpp>
#include <iostream>

int main()
{
    using namespace std;
    using namespace boost;

    cout << "ratio_string<deca, char>::prefix() = "
         <<  ratio_string<deca, char>::prefix() << '\n';
    cout << "ratio_string<deca, char>::symbol() = "
         <<  ratio_string<deca, char>::symbol() << '\n';

    cout << "ratio_string<giga, char>::prefix() = "
         <<  ratio_string<giga, char>::prefix() << '\n';
    cout << "ratio_string<giga, char>::symbol() = "
         <<  ratio_string<giga, char>::symbol() << '\n';

    cout << "ratio_string<ratio<4, 6>, char>::prefix() = "
         <<  ratio_string<ratio<4, 6>, char>::prefix() << '\n';
    cout << "ratio_string<ratio<4, 6>, char>::symbol() = "
         <<  ratio_string<ratio<4, 6>, char>::symbol() << '\n';
}

The output will be

ratio_string<deca, char>::prefix() = deca
ratio_string<deca, char>::symbol() = da
ratio_string<giga, char>::prefix() = giga
ratio_string<giga, char>::symbol() = G
ratio_string<ratio<4, 6>, char>::prefix() = [2/3]
ratio_string<ratio<4, 6>, char>::symbol() = [2/3]

This example illustrates the use of type-safe physics code interoperating with boost::chrono::duration types, taking advantage of the Boost.Ratio infrastructure and design philosophy.

Let's start by defining a length class template that mimics boost::chrono::duration, which represents a time duration in various units, but restricts the representation to double and uses Boost.Ratio for length unit conversions:

template <class Ratio>
class length {
private:
    double len_;
public:
    typedef Ratio ratio;
    length() : len_(1) {}
    length(const double& len) : len_(len) {}

    template <class R>
    length(const length<R>& d)
            : len_(d.count() * boost::ratio_divide<Ratio, R>::type::den /
                               boost::ratio_divide<Ratio, R>::type::num) {}

    double count() const {return len_;}

    length& operator+=(const length& d) {len_ += d.count(); return *this;}
    length& operator-=(const length& d) {len_ -= d.count(); return *this;}

    length operator+() const {return *this;}
    length operator-() const {return length(-len_);}

    length& operator*=(double rhs) {len_ *= rhs; return *this;}
    length& operator/=(double rhs) {len_ /= rhs; return *this;}
};

Here's a small sampling of length units:

typedef length<boost::ratio<1> >          meter;        // set meter as "unity"
typedef length<boost::centi>              centimeter;   // 1/100 meter
typedef length<boost::kilo>               kilometer;    // 1000  meters
typedef length<boost::ratio<254, 10000> > inch;         // 254/10000 meters

Note that since length's template parameter is actually a generic ratio type, so we can use boost::ratio allowing for more complex length units:

typedef length<boost::ratio_multiply<boost::ratio<12>, inch::ratio>::type>   foot;  // 12 inchs
typedef length<boost::ratio_multiply<boost::ratio<5280>, foot::ratio>::type> mile;  // 5280 feet

Now we need a floating point-based definition of seconds:

typedef boost::chrono::duration<double> seconds;                         // unity

We can even support sub-nanosecond durations:

typedef boost::chrono::duration<double,  boost::pico> picosecond;  // 10^-12 seconds
typedef boost::chrono::duration<double, boost::femto> femtosecond; // 10^-15 seconds
typedef boost::chrono::duration<double,  boost::atto> attosecond;  // 10^-18 seconds

Finally, we can write a proof-of-concept of an SI units library, hard-wired for meters and floating point seconds, though it will accept other units:

template <class R1, class R2>
class quantity
{
    double q_;
public:
    typedef R1 time_dim;
    typedef R2 distance_dim;
    quantity() : q_(1) {}

    double get() const {return q_;}
    void set(double q) {q_ = q;}
};

template <>
class quantity<boost::ratio<1>, boost::ratio<0> >
{
    double q_;
public:
    quantity() : q_(1) {}
    quantity(seconds d) : q_(d.count()) {}  // note:  only User1::seconds needed here

    double get() const {return q_;}
    void set(double q) {q_ = q;}
};

template <>
class quantity<boost::ratio<0>, boost::ratio<1> >
{
    double q_;
public:
    quantity() : q_(1) {}
    quantity(meter d) : q_(d.count()) {}  // note:  only User1::meter needed here

    double get() const {return q_;}
    void set(double q) {q_ = q;}
};

template <>
class quantity<boost::ratio<0>, boost::ratio<0> >
{
    double q_;
public:
    quantity() : q_(1) {}
    quantity(double d) : q_(d) {}

    double get() const {return q_;}
    void set(double q) {q_ = q;}
};

That allows us to create some useful SI-based unit types:

typedef quantity<boost::ratio<0>, boost::ratio<0> >  Scalar;
typedef quantity<boost::ratio<1>, boost::ratio<0> >  Time;         // second
typedef quantity<boost::ratio<0>, boost::ratio<1> >  Distance;     // meter
typedef quantity<boost::ratio<-1>, boost::ratio<1> > Speed;        // meter/second
typedef quantity<boost::ratio<-2>, boost::ratio<1> > Acceleration; // meter/second^2

To make quantity useful, we need to be able to do arithmetic:

template <class R1, class R2, class R3, class R4>
quantity<typename boost::ratio_subtract<R1, R3>::type,
         typename boost::ratio_subtract<R2, R4>::type>
operator/(const quantity<R1, R2>& x, const quantity<R3, R4>& y)
{
    typedef quantity<typename boost::ratio_subtract<R1, R3>::type,
                    typename boost::ratio_subtract<R2, R4>::type> R;
    R r;
    r.set(x.get() / y.get());
    return r;
}

template <class R1, class R2, class R3, class R4>
quantity<typename boost::ratio_add<R1, R3>::type,
         typename boost::ratio_add<R2, R4>::type>
operator*(const quantity<R1, R2>& x, const quantity<R3, R4>& y)
{
    typedef quantity<typename boost::ratio_add<R1, R3>::type,
                    typename boost::ratio_add<R2, R4>::type> R;
    R r;
    r.set(x.get() * y.get());
    return r;
}

template <class R1, class R2>
quantity<R1, R2>
operator+(const quantity<R1, R2>& x, const quantity<R1, R2>& y)
{
    typedef quantity<R1, R2> R;
    R r;
    r.set(x.get() + y.get());
    return r;
}

template <class R1, class R2>
quantity<R1, R2>
operator-(const quantity<R1, R2>& x, const quantity<R1, R2>& y)
{
    typedef quantity<R1, R2> R;
    R r;
    r.set(x.get() - y.get());
    return r;
}

With all of the foregoing scaffolding, we can now write an exemplar of a type-safe physics function:

Distance
compute_distance(Speed v0, Time t, Acceleration a)
{
    return v0 * t + Scalar(.5) * a * t * t;  // if a units mistake is made here it won't compile
}

Finally, we can exercise what we've created, even using custom time durations (User1::seconds) as well as Boost time durations (boost::chrono::hours). The input can be in arbitrary, though type-safe, units, the output is always in SI units. (A complete Units library would support other units, of course.)

int main()
{
    typedef boost::ratio<8, BOOST_INTMAX_C(0x7FFFFFFFD)> R1;
    typedef boost::ratio<3, BOOST_INTMAX_C(0x7FFFFFFFD)> R2;
    typedef User1::quantity<boost::ratio_subtract<boost::ratio<0>, boost::ratio<1> >::type,
                             boost::ratio_subtract<boost::ratio<1>, boost::ratio<0> >::type > RR;
    typedef boost::ratio_subtract<R1, R2>::type RS;
    std::cout << RS::num << '/' << RS::den << '\n';


    std::cout << "*************\n";
    std::cout << "* testUser1 *\n";
    std::cout << "*************\n";
    User1::Distance d( User1::mile(110) );
    User1::Time t( boost::chrono::hours(2) );

    RR r=d / t;
    //r.set(d.get() / t.get());

    User1::Speed rc= r;

    User1::Speed s = d / t;
    std::cout << "Speed = " << s.get() << " meters/sec\n";
    User1::Acceleration a = User1::Distance( User1::foot(32.2) ) / User1::Time() / User1::Time();
    std::cout << "Acceleration = " << a.get() << " meters/sec^2\n";
    User1::Distance df = compute_distance(s, User1::Time( User1::seconds(0.5) ), a);
    std::cout << "Distance = " << df.get() << " meters\n";
    std::cout << "There are "
        << User1::mile::ratio::den << '/' << User1::mile::ratio::num << " miles/meter";
    User1::meter mt = 1;
    User1::mile mi = mt;
    std::cout << " which is approximately " << mi.count() << '\n';
    std::cout << "There are "
        << User1::mile::ratio::num << '/' << User1::mile::ratio::den << " meters/mile";
    mi = 1;
    mt = mi;
    std::cout << " which is approximately " << mt.count() << '\n';
    User1::attosecond as(1);
    User1::seconds sec = as;
    std::cout << "1 attosecond is " << sec.count() << " seconds\n";
    std::cout << "sec = as;  // compiles\n";
    sec = User1::seconds(1);
    as = sec;
    std::cout << "1 second is " << as.count() << " attoseconds\n";
    std::cout << "as = sec;  // compiles\n";
    std::cout << "\n";
    return 0;
}

See the source file example/si_physics.cpp

C++ Standards Committee's current Working Paper

The most authoritative reference material for the library is the C++ Standards Committee's current Working Paper (WP). 20.6 Compile-time rational arithmetic "ratio"

N2661 - A Foundation to Sleep On

From Howard E. Hinnant, Walter E. Brown, Jeff Garland and Marc Paterno. Is very informative and provides motivation for key design decisions

LWG 1281. CopyConstruction and Assignment between ratios having the same normalized form

From Vicente Juan Botet Escriba.


PrevUpHomeNext