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 | 下向き丸めで文字列から変換 |
見ての通り、現在の実装は例えばadd_upは、
static T add_up(const T& x, const T& y) { return x + y; }となっており、まったく丸めをケアしていない。よって、例えばTが有理数などの 誤差の発生しない型ならばこのままでもよいが、Tがdoubleの場合はこのままでは 精度保証は出来ない。
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で定義された型に対する区間演算のカスタマイズの良い 例になっている。