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で定義された型に対する区間演算のカスタマイズの良い 例になっている。