読者です 読者をやめる 読者になる 読者になる

meryngii.neta

今日も新たな"ネタ"を求めて。

丸め指定された数値演算

C++ C++0x

mailing2008-12が来ました。
Directed Rounding Arithmetic Operationsという提案があるようです。
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2811.pdf
この提案の動機はC++0xで区間演算*1がサポートされることになったことです。C99からfenv.h(cfenv)というヘッダを取り入れ浮動小数点の丸め誤差について指定できるようになったので、これを区間演算でも使うらしいのですが、高速な区間演算のためには問題があるというのです。

しかし区間演算の高性能なサポートを妨げる二つの問題がある:
・Committee Draftではブロック内のコードで丸めモードについて注意するようにとコンパイラに指示するFENV_ACCESSプラグマをサポートしていない。その結果、volatileな一時変数やその他の等価な実装系依存の方法を使わなければならず、非効率的になる。これはC++03と比較されるC++の不完全な拡張であると考えられる。
・区間定数が作れれば便利だが、しかしconstexprで許される制限された形はfesetroundを呼ぶことを禁止しているので、constexprで区間演算はできない。これをサポートするために例えばconstexprへ一般的な拡張をすることは、この部分的な用途のために適しているようには見えない。

前者の問題点はゼロオーバーヘッドルールに違反することなのではないかと思われます。
後者について、constexprでは副作用が認められないので、丸めモードをグローバルに指定するfesetroundのような関数は当然呼び出せないわけです。
この提案では後者の解決策を提案しています。簡単にいえば、C99の丸めモードの指定と分離した演算関数を作ってしまおうという話のようです。
浮動小数点の丸め方には四種類あるわけですが、

  • 一番近い方(デフォルト)
  • 0に近い方
  • ∞に近い方
  • -∞に近い方

これらを指定するfloating_round_styleというenumを用意して、これを演算する関数にテンプレート引数で渡すわけです。

template < float_round_style r, FloatingPointLike T, FloatingPointLike U >
requires True<(r != round_indeterminate)>
constexpr auto add(T t, U u) -> decltype(t + u);

double d = add<round_toward_infinity>(a, b);

そして、この後の実装はコンパイラに任せるというわけです。演算子にテンプレート引数を渡すことはできないので、名前を付けるしかないというところでしょうか。あまりしっくり来ない感じです。そしてこれを定義するためにというヘッダを追加するようです。
私が気になったのはsqrtがあることなのですが、

template < float_round_style r, FloatingPointLike T >
requires True<(r != round_indeterminate)>
constexpr T sqrt(T t);

様々ある数学関数の中でなぜ平方根だけなのか疑問です。またのstd::sqrtとは衝突しないのでしょうか。
あとsqrtなんかを作るにはconstexprの再帰が必要だと思うのですが、もしかしてコンパイラに丸投げするつもりなんですかねえ…。
http://d.hatena.ne.jp/meryngii/20081202/1228228226

*1:区間同士を演算する考え方で、Boostでもサポートされているようです。 http://www.kmonos.net/alang/boost/classes/interval.html