Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

Click here to view the latest version of this page.
PrevUpHomeNext

User's Guide

Getting Started
Tutorial
Example
External Resources
Getting Boost.Ratio

Boost.Ratio is in the latest Boost release in the folder /boost/ratio. Documentation, tests and examples folder are at boost/libs/ratio/.

You can also access the latest (unstable?) state from the Boost trunk directories boost/ratio and libs/ratio.

Just go to the wiki and follow the instructions there for anonymous SVN access.

Where to install Boost.Ratio?

The simple way is to decompress (or checkout from SVN) the files in your BOOST_ROOT directory.

Building Boost.Ratio

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

Requirements

Boost.Ratio depends on some Boost libraries. For these specific parts you must use either Boost version 1.39.0 or later (even older versions may work).

In particular, Boost.Ratio depends on:

Boost.Config

for configuration purposes, ...

Boost.Integer

for cstdint conformance, and integer traits ...

Boost.MPL

for MPL Assert and bool, logical ...

Boost.StaticAssert

for STATIC_ASSERT, ...

Boost.TypeTraits

for is_base, is_convertible ...

Boost.Utility/EnableIf

for enable_if, ...

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.

Tested compilers

Boost.Ratio should work with an C++03 conforming compiler. The current version has been tested on:

Windows with

  • MSVC 10.0
  • MSVC 9.0 Express
  • MSVC 8.0

Cygwin 1.5 with

  • GCC 3.4.4

Cygwin 1.7 with

  • GCC 4.3.4

MinGW with

  • GCC 4.4.0
  • GCC 4.5.0
  • GCC 4.5.0 -std=c++0x
  • GCC 4.6.0
  • GCC 4.6.0 -std=c++0x

Ubuntu 10.10

  • GCC 4.4.5
  • GCC 4.4.5 -std=c++0x
  • GCC 4.5.1
  • GCC 4.5.1 -std=c++0x
  • clang 2.8

Initial versions were tested on:

MacOS with GCC 4.2.4

Ubuntu Linux with GCC 4.2.4

[Note] Note

Please let us know how this works on other platforms/compilers.

[Note] Note

Please send any questions, comments and bug reports to boost <at> lists <dot> boost <dot> org.

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: short_name and long_name. For those ratio's that correspond to an SI prefix long_name corresponds to the internationally recognized prefix, stored as a basic_string<CharT>. For example ratio_string<mega, char>::long_name() returns string("mega"). For those ratios that correspond to an SI prefix short_name corresponds to the internationally recognized symbol, stored as a basic_string<CharT>. For example, ratio_string<mega, char>::short_name() returns string("M"). For all other ratios, both long_name() and short_name() 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 short_name (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>::long_name() = "
         <<  ratio_string<deca, char>::long_name() << '\n';
    cout << "ratio_string<deca, char>::short_name() = "
         <<  ratio_string<deca, char>::short_name() << '\n';

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

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

The output will be

ratio_string<deca, char>::long_name() = deca
ratio_string<deca, char>::short_name() = da
ratio_string<giga, char>::long_name() = giga
ratio_string<giga, char>::short_name() = G
ratio_string<ratio<4, 6>, char>::long_name() = [2/3]
ratio_string<ratio<4, 6>, char>::short_name() = [2/3]
Ratio MPL Numeric Metafunctions

With the view of the _ratio class as a Rational Constant we can mix _ratio<> and Boost.MPL Integral Constants in the same expression, as in

typedef mpl::times<int_<5>, giga>::type _5giga;  
// _5giga::num == 5000000000, _5giga::den == 1

typedef mpl::times<int_<5>, nano>::type _5nano;  
// _5nano::num == 1, _5nano::den == 200000000

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