最終更新: 2026/5/27

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浮動小数点数を活用する (kv::fp80)

前述したようにVisual C++では封印されてしまいましたが、gccやclangでは 今でも80bit浮動小数点数を使うことができます。 ところで、long doubleという名前は、Visual C++ではdoubleと同じだったり、 またIntel以外のアーキテクチャでは128bit浮動小数点数だったりすることもあり、 何に対応しているかは混乱を極めています。

version 0.4.55までは80bit浮動小数点数を使うのに_Float64xという型を 使っていました。しかし、g++ 13以降、_Float64xはlibstdc++と適合しなくなり、 この型は使えなくなってしまい、80bit浮動小数点数関連の機能は g++13以降では使えないように封印しました。

version 0.4.60で、long doubleを使うことによりこの機能を復活させました。 今後、どの型で使うのが最適か変わる可能性もあるため、 kv::fp80を定義して(現在はlong doubleのaliasになっている)、それを使って 記述する方針にしました。 80bit浮動小数点を使うには、

#include <kv/fp80.h>
をincludeします。このheader fileでは80bit浮動小数点数が使えるかどうか 判定し、使える場合はマクロKV_HAVE_FP80を定義して、
using kv::fp80 = long double;
のように定義します。プログラム中ではlong double, __float80, _Float64x などの具体的な型を使わずに、kv::fp80を使って書いておけば、 今後状況が変わったときにも最小限の変更で済みます。

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

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

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

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

いわゆるdouble-double (dd)は、53bitのdoubleを2つ繋げて擬似的に 106bit相当の演算を行うものですが、同様にkv::fp80を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をkv::fp80に、あるいはddをddxに変えたときの速度低下はせいぜい 10-20%程度で、ほとんど変わりません。あと少しだけ精度が足りないけれど、 mpfrでは遅すぎてつらい、というときに役に立つかもしれません。

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

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

(version 0.4.60より) g++ version 13以降で使えなくなっていたのを、 _Float64xを使うのを止めてlong doubleにして復活させました。 今後、どの型で使うのが最適か変わる可能性もあるため、 kv::fp80を定義して(現在はlong doubleのaliasになっている)、それを使って 記述する方針にしました。それに伴い、

古い名前 新しい名前
rfloat64x.hpp rfp80.hpp
test-ifloat64x.cc test-ifp80.cc
conv-float64x.hpp conv-fp80.hpp

のようにいくつかのファイル名を変更しました。 g++13でこの機能は封印してしまったので利用してた方はほとんどいないとは 思いますが、もし利用していた場合はincludeするheader fileを変更し、 _Float64xでなくkv::fp80を使うように書き換えて下さい。