Home | Libraries | People | FAQ | More |
Boost.Ratio is part of Boost, so you get it as part of the Boost release.
Boost.Ratio is a header only library,
so no need to compile anything, you just need to include
<boost/ratio.hpp>
.
No link is needed.
All functions in the library are exception-neutral, providing the strong exception safety guarantee.
All functions in the library are thread-unsafe except when noted explicitly.
Boost.Ratio should work with any C++11 conforming compiler.
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:
typedefratio
<5, 3> five_thirds; // five_thirds::num == 5, five_thirds::den == 3 typedefratio
<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
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 ratio
s 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 ratio
s, 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
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"
From Howard E. Hinnant, Walter E. Brown, Jeff Garland and Marc Paterno. Is very informative and provides motivation for key design decisions
From Vicente Juan Botet Escriba.