最近 Rust という言語を学んでいます。システムプログラム向けということらしいですが、私はパフォーマンスからして数値計算にも向いているのではないかと勝手に考えています。
行列計算プログラムでも書けばいいかもしれませんが、偶然出会った double-double 演算に興味を持ったので、 Rust で quad-double の加算を実装してきました。 C++ による実装はすでにありますので参考にしつつ。
quad-double 演算について
quad-double (以下 QD と書く)演算は、倍精度浮動小数点( double )を 4 つ使うことにより、精度の高い演算を実現します。 2 つ使ったものが double-double 演算です。
4倍精度浮動小数点( gcc での long double )や 8 倍精度浮動小数点数とは違って値のとれる範囲は増えませんが、精度の必要な計算に対するソフトウェア的な手法として使われることがあるようです。
Rust では double は f64
に当たりますが、たまに Rust への要望に出てくる f128
は 4 倍精度のほうだと思われます。
データ構造の実装
QD 演算の実装に際し、論文に対応するよう 3 つの構造体と関数を作成しました。
1 |
|
0 | // omit |
30 |
|
SumError
は f64
の加算の結果と情報落ちのペアです。 QD 数は演算直後は 5 つの f64
で表される展開形式 ExtendedQuadDouble
で表され、それを唯一に定まる正規化表現 NormalizedQuadDouble
に変換します。
double 同士の加算
いつもの double 同士の加算では暗黙の了解で誤差を無視してしまっていますが、誤差について考えると、条件によって誤差の算出に必要な演算回数が違う 2 つの加算が生まれます。
13 |
|
quick_two_sum()
は が成り立つとき、 two_sum()
はいつでも使えます。正規化関数 renormalize()
では quick_two_sum()
を使うことで演算回数を減らしています。
QD 数との加算
ここでは QD 数と double の加算と QD 数同士の加算を実装しています。
54 |
|
実行例
128 | fn main() { |
1 | 7000000000.0000000000 |
まず情報落ちの例として、 3E-9_f64 + 7E+9_f64
の結果が 7000000000.0000000000
となり、 double 同士の加算では 3E-9_f64
の情報が消えていることがわかります。
QD 数と double の加算については、しっかり 3E-9_f64
の情報が保存されていることがわかります。
QD 数同士の加算については、多少の誤差が出てしまいましたが、なんとか 3E+18_f64
、 5E+9_f64
、 7E-9_f64
、 9E-18_f64
の存在がわかります。