最終更新: 2024/2/7
Intel 80bit浮動小数点数を活用する
柏木 雅英
1. はじめに
Intel製のCPUは、
- 浮動小数点演算コプロセッサ8087以来の、FPU
- SSE2
の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関連の機能を
使えないようにしました。