Boost C++ 库

……这是世界上备受推崇且设计精良的 C++ 库项目之一。 Herb SutterAndrei Alexandrescu, C++ Coding Standards

用户指南 - Boost C++ 函数库
PrevUpHomeNext
获取 Boost.Ratio

Boost.Ratio 是 Boost 的一部分,因此您可以在 Boost 发行版中获取它。

构建 Boost.Ratio

Boost.Ratio 是一个仅头文件库,因此无需编译任何内容,您只需 include <boost/ratio.hpp>

构建使用 Boost.Ratio 的可执行文件

无需链接。

异常安全

库中的所有函数都具有异常中性,并提供强异常安全保证。

线程安全

除非另有明确说明,否则库中的所有函数都是线程不安全的。

支持的编译器

Boost.Ratio 应与任何符合 C++11 标准的编译器配合使用。

Ratio

ratio 是一个通用的实用程序,其灵感来自 Walter Brown,它允许用户在编译时轻松安全地计算有理数值。ratio 类在编译时捕获所有错误(例如除以零和溢出)。它用于 duration 和 time_point 类,以高效地创建时间单位。它还可以用于其他“数量”库或任何已知有理常数的位置。使用此实用程序可以大大降低运行时溢出的可能性,因为 ratio(以及由 ratio 算术产生的任何 ratio)始终被简化为最低项。

ratio 是一个接受两个 intmax_ts 的模板,第二个默认为 1。除了复制构造函数和赋值运算符外,它只有两个公共成员,两者都是 static const intmax_t。一个是 ratio 的分子,另一个是分母。ratio 始终被规范化,使其以最低项表示,并且分母始终为正。当分子为 0 时,分母始终为 1。

示例

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

此功能还包括 SI 前缀从 attoexa 的便捷类型定义,这些定义对应于它们在国际上公认的定义(相对于 ratio)。这是一个巨大的语法便利。它将防止在指定常量时出错,因为在尝试编写数百万或数十亿时,您不再需要重复计算零的数量。

示例

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

对于每个 ratio<N, D>,都存在一个 ratio_string<ratio<N, D>, CharT>,您可以从中查询两个字符串:symbolprefix。对于那些对应于 SI 前缀ratioprefix 对应于国际公认的前缀,存储为 basic_string<CharT>。例如 ratio_string<mega, char>::prefix() 返回 string("mega")。对于那些对应于 SI 前缀ratiosymbol 对应于国际公认的符号,存储为 basic_string<CharT>。例如,ratio_string<mega, char>::symbol() 返回 string("M")。对于所有其他 ratioprefix()symbol() 都返回一个 basic_string,其中包含“[ratio::num/ratio::den]”。

ratio_string<ratio<N, D>, CharT> 仅为四种字符类型定义:

  • char:UTF-8
  • char16_t:UTF-16
  • char32_t:UTF-32
  • wchar_t:UTF-16(如果 wchar_t 为 16 位)或 UTF-32

当字符为 char 时,将使用 UTF-8 对名称进行编码。当字符为 char16_t 时,将使用 UTF-16 对名称进行编码。当字符为 char32_t 时,将使用 UTF-32 对名称进行编码。当字符为 wchar_t 时,如果 wchar_t 为 16 位,则编码为 UTF-16;否则为 UTF-32。

symbol(希腊字母 mu 或 μ)用于表示微(micro),根据 Unicode 定义为 U+00B5。

示例

#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';
}

输出将是:

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]

此示例演示了如何使用类型安全的物理代码与 boost::chrono::duration 类型进行互操作,并利用了 Boost.Ratio 的基础设施和设计理念。

让我们首先定义一个 length 类模板,它模仿 boost::chrono::duration,它表示各种单位的时间持续时间,但将表示限制为 double,并使用 Boost.Ratio 进行长度单位转换。

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;}
};

这里是一些长度单位的示例:

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

请注意,由于 length 的模板参数实际上是一个通用的 ratio 类型,因此我们可以使用 boost::ratio,从而支持更复杂的长度单位。

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

现在我们需要一个基于浮点数的秒定义。

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

我们甚至可以支持亚纳秒持续时间。

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

最后,我们可以编写一个 SI 单位库的概念验证,该库硬编码为以米和浮点秒为单位,尽管它也可以接受其他单位。

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;}
};

这使我们能够创建一些有用的基于 SI 的单位类型。

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

为了使数量有用,我们需要能够进行算术运算。

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;
}

有了以上所有基础架构,我们现在就可以编写一个类型安全物理函数的示例了。

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
}

最后,我们可以测试我们创建的内容,甚至使用自定义时间持续时间(User1::seconds)以及 Boost 时间持续时间(boost::chrono::hours)。输入可以是任意类型安全的单位,输出始终是 SI 单位。(当然,一个完整的单位库会支持其他单位。)

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;
}

请参阅源文件 example/si_physics.cpp

C++ 标准委员会当前的工件

该库最权威的参考资料是 C++ 标准委员会当前的工件(WP)。20.6 编译时有理数算术“ratio”

N2661 - A Foundation to Sleep On

来自 Howard E. Hinnant、Walter E. Brown、Jeff Garland 和 Marc Paterno。非常翔实,并解释了关键设计决策的动机。

LWG 1281. 具有相同规范化形式的 ratio 之间的 CopyConstruction 和 Assignment

来自 Vicente Juan Botet Escriba。


PrevUpHomeNext