最終更新: 2013/10/18

区間演算ライブラリのカスタマイズ

柏木 雅英

1. はじめに

includeするheader fileを差し替えることによって、丸めの方法を 変更することが出来る。

2. ropクラス

interval.hppの中に、rop (rounnding operations) クラスが定義されている。

template <class T> struct rop {

    static T add_up(const T& x, const T& y) {
        return x + y;
    }

    static T add_down(const T& x, const T& y) {
        return x + y;
    }

    static T sub_up(const T& x, const T& y) {
        return x - y;
    }

    static T sub_down(const T& x, const T& y) {
        return x - y;
    }

    static T mul_up(const T& x, const T& y) {
        return x * y;
    }

    static T mul_down(const T& x, const T& y) {
        return x * y;
    }

    static T div_up(const T& x, const T& y) {
        return x / y;
    }

    static T div_down(const T& x, const T& y) {
        return x / y;
    }

    static T sqrt_up(const T& x) {
        return sqrt(x);
    }

    static T sqrt_down(const T& x) {
        return sqrt(x);
    }

    static void begin() {
    }

    static void end() {
    }

    static void print_up(const T& x, std::ostream& s) {
        s << x;
    }

    static void print_down(const T& x, std::ostream& s) {
        s << x;
    }

    static T fromstring_up(const std::string& s) {
        std::istringstream is(s);
        T r;
        is >> r;
        return r;
    }

    static T fromstring_down(const std::string& s) {
        std::istringstream is(s);
        T r;
        is >> r;
        return r;
    }
};

これはテンプレートクラスになっており、型Tに対する、以下の機能を どう実行するかを定義している:

add_up上向き丸めの加算
add_down下向き丸めの加算
sub_up上向き丸めの減算
sub_down下向き丸めの減算
mul_up上向き丸めの乗算
mul_down下向き丸めの乗算
div_up上向き丸めの除算
div_down下向き丸めの除算
sqrt_up上向き丸めの平方根
sqrt_down下向き丸めの平方根
begin(必要ならば)丸めモード変更の準備
end(必要ならば)丸めモード変更の終了
print_up上向き丸めで表示
print_down下向き丸めで表示
fromstring_up上向き丸めで文字列から変換
fromstring_down下向き丸めで文字列から変換

3. カスタマイズ

カスタマイズは、このropクラスを部分特殊化し、特定の型に対するropを上書きする ことによって行う。

見ての通り、現在の実装は例えばadd_upは、

    static T add_up(const T& x, const T& y) {
        return x + y;
    }
となっており、まったく丸めをケアしていない。よって、例えばTが有理数などの 誤差の発生しない型ならばこのままでもよいが、Tがdoubleの場合はこのままでは 精度保証は出来ない。

4. rdouble.hpp

rdouble.hppは、T=doubleに対するちゃんと精度保証出来るようなカスタマイズの一例 である。一番よく使われるであろう「両端にdoubleを持つ区間演算」は、

#include <kv/interval.hpp>
#include <kv/rdouble.hpp>
のように2つのheader fileを読み込むことにより実現できる。

rdouble.hppでは、丸めのモードを上向きのみを使うことによって 丸めの変更の回数を減らしている。すなわち、beginで上向きに変更し、 endで最近点に戻す実装になっている。 このとき、例えばadd_downは -((-x)+(-y))のような計算を行うことになるので、 volatileを使って最適化を抑制している。詳細はrdouble.hppを見て欲しい。

(独り言) rdouble.hppでincludeしているconv-double.hppは、doubleと文字列との 相互変換を丸めモード指定で行える。ここに含まれるdtostring, stringtodは 単独で抜き出しても有用な関数と思われる。ぜひ活用してほしい。

別の丸めの方法で区間演算を行いたいと思ったら、 rdouble.hppをincludeせずに、 別の方法でropクラスをカスタマイズしたファイルを準備し、 それをincludeすれば良い。

rdd.hppは、dd.hppで定義された型に対する区間演算のカスタマイズの良い 例になっている。