WisdomSoft - for your serial experiences.

11.3 strictfp修飾子

既定では Java による浮動小数点数の演算であっても、その結果は CPU 固有の浮動小数点数演算回路に依存します。strictfp 修飾子を用いることで、常に IEEE 754 に従った結果を得られます。

11.3.1 FP-strict式

Java はどこでも動くソフトウェアを唱える仮想マシンであることを、これまで何度も強調して説明しました。これは、コンピュータのもっとも基本的な演算部分においても同じです。仮想マシンの計算結果は常に同じであることが保障されなければなりません。ところが、浮動小数点の計算で Java の仕様に合わせていると、仮想マシンが CPU の機能を十分に発揮させることができないことがあります。科学技術演算や事務処理、3次元コンピュータ・グラフィックスの分野では浮動小数点数の演算速度が重要となるため、これは大きな問題とされてきました。

Java 言語では、すべての実装が提供しなければならない float 型と double 型の浮動小数点数値の表現方法を定めています。これを float 数値集合 及び double 数値集合と呼び、これらを合わせて標準集合と呼びます。これに加え、コンピュータの CPU が標準集合よりも精度の高い内部表現を持つ場合、オプションとしてこれを利用することができるように指数拡張 float 数値集合指数拡張 double 数値集合と呼ばれる拡張方式も定めているのです。これらを指数拡張浮動小数点数値集合と呼びます。

仮想マシンが指数拡張浮動小数点数値集合をサポートしている場合、指数拡張浮動小数点数値集合で定められている制約の範囲で、実装依存の内部表現が許容されます。この場合 float や double 型を含む式の中間結果の表現に指数拡張浮動小数点数値集合が使われるでしょう。もちろん、最終的な結果は Java が定める float または double 型として扱われるため、これは式の計算過程の問題です。仕様書を読めば、数値集合の形式と制約範囲を知ることができますが、一般的なアプリケーションプログラマは意識する必要はありません。

比較的新しい Java 言語仕様では、この拡張表現の利用を拒否することもできます。拡張表現を利用しないことに大きなメリットはありませんが、計算結果がどの時点においても同じであることが要求されるならば、常に標準集合で計算される必要があります。例えば、標準集合ならばオーバーフローを起こす中間結果が、精度の高い指数拡張浮動小数点数値集合が使われたために正しい結果が得られるなどのケースが考えられます。こうした計算過程の誤差が問題になるようならば、strictfp 修飾子を用いて中間結果を同一にする必要があります。

拡張表現を利用しない、標準集合だけを用いる式を FP-strict 式と呼びます。strictfp 修飾子は、ブロック内の浮動小数点数型を含む式が常に FP-strict 式であることを要求します。strictfp 修飾子は、クラス、インタフェース、メソッドの修飾子に指定することができます。ただし、コンストラクタに指定することはできないので注意してください。

strictfp 修飾子が指定されたブロック内のすべての式は FP-strict 式となります。クラスに strictfp 修飾子が指定されている場合は、クラス内のすべてのコードが FP-strict 式を利用するようになります。静的初期化子や変数初期化子、コンストラクタ内の式を FP-strict 式にしたい場合、クラスに strictfp 修飾子を指定する必要があります。また、コンパイル時の定数式は自動的に FP-strict 式となります。

コード1
class Test {
	public static final float f = 0.3F * 10F; //FP-strict 式
	public strictfp static void main(String args[]) {
		double d = 1.7E+308;
		System.out.println(d * f * 0.1); //FP-strict 式
		System.out.println(d * 0.1 * f); //FP-strict 式
	}
}
実行結果
>java Test
Infinity
5.1E307

コード1の main() メソッドは、宣言で strictfp 修飾子を指定しています。そのため、このメソッド内の浮動小数点数が他の計算はすべて FP-strict 式となります。

Java の浮動小数型は IEEE 754 規格に関連づけられています。この規格は、浮動小数点数の表現方法を定める国際規格です。この規格に基づく浮動小数点数は数値以外に、正と負の 0、正と負の無限大(Infinity)、無効な演算結果をあらわす非数 NaN(Not a Number) を表現することができます。

実行結果を見ると、最初の計算結果がオーバーフローによる無限大を表しています。数学的には d * f * 0.1 と d * 0.1 * f の結果は同じものになるはずですが、プログラムを実行すると d * f を計算した時点で、計算結果の値が大きすぎるためにオーバーフローが発生し、無限大という結果が得られたのです。もし、指数拡張浮動小数点数値集合が中間結果に用いられれば、精度が高いためにオーバーフローを回避する可能性があります。この場合、計算結果は実装依存となってしまうでしょう。

strictfp は比較的新しい Java 言語で採用された仕様です。古い仮想マシンや古い開発環境ではサポートされていません。