最終更新: 2024/2/7

Intel 80bit浮動小数点数を活用する

柏木 雅英

1. はじめに

Intel製のCPUは、 の2種類の浮動小数点演算器を持っていて、前者は古く後者は新しく 追加されたものです。 前者は、内部演算が全長80bit、指数部15bit、仮数部64bitの拡張倍精度 になっていて、これがいわゆるIEEE754の倍精度よりも精度が良いために、 逆にさまざまな精度の問題を引き起こしてきました。 SSE2の方は、完全にIEEE754に従っています。 64bit命令が使用可能なCPUはすべてSSE2命令を持っているため、 OSの64bit化のタイミングでFPUは基本的に使わずすべてSSE2で処理するのが 普通になり、FPUは存在するが使われない盲腸のような存在になりました。 また、かつてはFPUの演算器をlong doubleとして80bitのまま使うことができましたが、 現在ではMicrosoftのVisual C++では使えなくなっています (long double = doubleになっている)。 gccやclangでは今でも使えます。

kv-0.4.54で、このIntelの80bit浮動小数点演算器を活用するための いくつかのファイルを追加しました。

2. 80bit浮動小数点数を活用する (_Float64x)

前述したようにVisual C++では封印されてしまいましたが、gccやclangでは 今でも80bit浮動小数点数を使うことができます。 ところで、long doubleという名前は、Visual C++ではdoubleと同じだったり、 またIntel以外のアーキテクチャでは128bit浮動小数点数だったりすることもあり、 何に対応しているかは混乱を極めています。 そこで、最近はこの80bit演算を明示的に示すための_Float64xという型名が 提案されています。 kvでは、混乱を避けるためこの名前で80bit浮動小数点数を扱う ことにします。 比較的新しいコンパイラでないと使えないかもしれません。 cmathヘッダファイルをincludeして、__HAVE_FLOAT64Xというマクロが定義されて いる場合に使用することができます。

3. 80bit浮動小数点数を両端に持つ区間演算 (rfloat64x.hpp)

doubleでの区間演算は、interval.hppの後にrdouble.hppをincludeすると kv::interval<double>という型で行えますが、同様に _Float64xでの区間演算はinterval.hppの後にrfloat64x.hppをincludeすると kv::interval<_Float64x>という型で行えます。

test/test-ifloat64x.cc が、使用方法のサンプルになっています。 test-interval.ccや、test-idd.ccとほとんど同じで、最初のincludeと 型名が違うだけです。

4. _Float64xを2つ繋げた128bit浮動小数点演算 (ddx.hpp)

いわゆるdouble-double (dd)は、53bitのdoubleを2つ繋げて擬似的に 106bit相当の演算を行うものですが、同様に_Float64xを2つ繋げて 指数部15bit、仮数部128bit相当の浮動小数点演算 (ddx = double double-extended) を作ってみました。

test/test-ddx.ccが使用例になっています。kv/ddx.hppをincludeすると、 kv::ddxという型が使えるようになります。ddとほぼ同じ使い勝手です。 数学関数も使えます。

5. ddx型を両端に持つ区間演算 (rddx.hpp)

ddx型での区間演算は、interval.hppの後にrddx.hppをincludeすると kv::interval<kv::ddx>という型で行えます。

test/test-iddx.cc が、使用方法のサンプルになっています。

6. おわりに

これらの型の追加で、区間演算を行う本体のkv/interval.hppには一切の変更を 行う必要はありませんでした。kvの区間演算周りの設計がうまくできていることを 示していると思います (自画自賛)。

doubleを_Float64xに、あるいはddをddxに変えたときの速度低下はせいぜい 10-20%程度で、ほとんど変わりません。あと少しだけ精度が足りないけれど、 mpfrでは遅すぎてつらい、というときに役に立つかもしれません。

rfloat64.hpp, rddx.hppで、 -DKV_NOHWROUNDをつけて丸めの向きの変更を一切行わさせない区間演算は 原理的には実装可能ですが、まだ実装していません。

(version 0.4.56より) g++ version 13で_Float64xの動作が異常に なってしまったので、g++ version 13以降では_Float64x関連の機能を 使えないようにしました。