浮動小数点演算

Under TheHoodの別の記事へようこそ。このコラムは、Java開発者が実行中のJavaプログラムの下に隠された美しさを垣間見ることを目的としています。今月のコラムでは、先月から始まったJava仮想マシン(JVM)のバイトコード命令セットについての議論を続けています。この記事では、JVMの浮動小数点演算について説明し、浮動小数点演算を実行するバイトコードについて説明します。以降の記事では、バイトコードファミリの他のメンバーについて説明します。

主なフローティングポイント

JVMの浮動小数点サポートは、IEEE-7541985浮動小数点標準に準拠しています。この標準は、32ビットおよび64ビットの浮動小数点数の形式を定義し、それらの数に対する演算を定義します。JVMでは、浮動小数点演算は32ビットのfloatと64ビットのdoubleで実行されます。floatで算術演算を実行するバイトコードごとに、doubleで同じ操作を実行する対応するバイトコードがあります。

浮動小数点数には、符号、仮数、基数、および指数の4つの部分があります。記号は1または-1のいずれかです。仮数は常に正の数であり、浮動小数点数の有効桁数を保持します。指数は、仮数と符号に乗算する必要がある基数の正または負の累乗を示します。4つのコンポーネントを次のように組み合わせて、浮動小数点値を取得します。

符号*仮数*基数指数

浮動小数点数には複数の表現があります。これは、浮動小数点数の仮数に基数の累乗を常に乗算し、指数を変更して元の数を取得できるためです。たとえば、数値-5は、基数10の次の形式のいずれかで等しく表すことができます。

-5の形式
符号 仮数 基数指数
-1 50 10 -1
-1 5 10 0
-1 0.5 10 1
-1 0.05 10 2

浮動小数点数ごとに、正規化されていると言われる表現が1つあります。仮数が次の関係で定義された範囲内にある場合、浮動小数点数は正規化されます。

1 /基数<=仮数<

正規化された基数10の浮動小数点数の小数点は、仮数の最初の非ゼロ桁のすぐ左にあります。-5の正規化された浮動小数点表現は-1 * 0.5 * 10 1です。言い換えると、正規化された浮動小数点数の仮数には、小数点の左側にゼロ以外の数字がなく、小数点の右側。このカテゴリに当てはまらない浮動小数点数は、非正規化されていると言われます。数値ゼロには、小数点のすぐ右側に配置するゼロ以外の数字がないため、正規化された表現がないことに注意してください。「なぜ正規化されるのですか?」ゼロの間でよくある感嘆符です。

JVMの浮動小数点数は、2の基数を使用します。したがって、JVMの浮動小数点数は、次の形式になります。

符号*仮数* 2指数

JVMの浮動小数点数の仮数は、2進数として表されます。正規化された仮数は、最上位の非ゼロ桁のすぐ左側に2進小数点(小数点に相当する2進数)を持ちます。2進数システムには0と1の2桁しかないため、正規化された仮数の最上位桁は常に1です。

floatまたはdoubleの最上位ビットは、その符号ビットです。仮数は、floatの23の最下位ビットと、doubleの52の最下位ビットを占めます。指数は、floatで8ビット、doubleで11ビットで、符号と仮数の間にあります。フロートのフォーマットを以下に示します。符号ビットは「s」として示され、指数ビットは「e」として示され、仮数ビットは「m」として示されます。

Javaフロートのビットレイアウト
s eeeeeeee mmmmmmmmmmmmmmmmmmmmmmm

ゼロの符号ビットは正の数を示し、1の符号ビットは負の数を示します。仮数は常に正の2進数として解釈されます。2の補数ではありません。符号ビットが1の場合、浮動小数点値は負ですが、仮数は正の数として解釈され、-1を掛ける必要があります。

指数フィールドは、3つの方法のいずれかで解釈されます。すべて1の指数は、浮動小数点数がプラスまたはマイナスの無限大、または「数値ではない」(NaN)の特別な値の1つを持っていることを示します。NaNは、ゼロによるゼロ除算などの特定の操作の結果です。すべてゼロの指数は、非正規化浮動小数点数を示します。その他の指数は、正規化された浮動小数点数を示します。

仮数には、仮数ビットに表示される精度を超える1ビットの精度が含まれています。 23ビットしか占有しないフロートの仮数は24ビットの精度を持っています。 52ビットを占めるdoubleの仮数は、53ビットの精度を持っています。 JVMの浮動小数点数の指数は、数値が正規化されているかどうかを示すため、最上位の仮数ビットは予測可能であり、したがって含まれていません。指数がすべてゼロの場合、浮動小数点数は非正規化され、仮数の最上位ビットはゼロであることがわかります。それ以外の場合、浮動小数点数は正規化され、仮数の最上位ビットは1であることがわかります。

JVMは、浮動小数点演算の結果として例外をスローしません。ゼロ除算などの疑わしい操作の結果として、正および負の無限大やNaNなどの特別な値が返されます。すべての指数は、特別な浮動小数点値を示します。ビットがすべてゼロである仮数を持つすべての指数は、無限大を示します。無限大の符号は符号ビットで示されます。他の仮数を持つすべてのものの指数は、「数ではない」(NaN)を意味すると解釈されます。JVMは、NaNに対して常に同じ仮数を生成します。これは、数値に現れる最上位の仮数ビットを除いて、すべてゼロです。これらの値は、以下のフロートについて示されています。

特別な浮動小数点値
フロートビット(指数仮数に署名)
+インフィニティ 0 11111111 00000000000000000000000
-インフィニティ 1 11111111 00000000000000000000000
NaN 1 11111111 10000000000000000000000

すべて1でもすべて0でもない指数は、正規化された仮数を乗算する2の累乗を示します。 2の累乗は、指数ビットを正の数として解釈し、正の数からバイアスを引くことによって決定できます。フロートの場合、バイアスは126です。ダブルの場合、バイアスは1023です。たとえば、フロートの00000001の指数フィールドは、正の整数として解釈される指数フィールドからバイアス(126)を引くことにより、2の累乗を生成します。 (1)。したがって、2の累乗は1〜126、つまり-125です。これは、フロートの2の可能な最小の累乗です。もう一方の極端な例では、11111110の指数フィールドは、(254-126)または128の2の累乗を生成します。数値128は、フロートで使用可能な2の最大の累乗です。正規化されたフロートのいくつかの例を次の表に示します。

正規化されたフロート値
フロートビット(指数仮数に署名) 偏りのない指数
最大の正(有限)フロート 0 11111110 11111111111111111111111 128
最大の負(有限)フロート 1 11111110 11111111111111111111111 128
最小の正規化されたフロート 1 00000001 00000000000000000000000 -125
円周率 0 10000000 10010010000111111011011 2

すべてゼロの指数は、仮数が非正規化されていることを示します。これは、記述されていない先行ビットが1ではなく0であることを意味します。この場合の2の累乗は、正規化された仮数で使用可能な2の累乗の最小値と同じです。フロートの場合、これは-125です。つまり、正規化された仮数に2を掛けて-125の累乗にすると、指数フィールドは00000001になり、非正規化された仮数に2を掛けて-125の累乗にすると、指数フィールドは00000000になります。下部の非正規化数の許容値指数の範囲の終わりは、段階的なアンダーフローをサポートします。代わりに、正規化された数値を表すために最小の指数が使用された場合、数値が大きいほどゼロへのアンダーフローが発生します。言い換えると、非正規化数の指数を最小にすることで、より小さな数を表すことができます。非正規化数が小さいほど、正規化数よりも精度のビットが少なくなりますが、指数が最小正規化値に達するとすぐにゼロにアンダーフローするよりも、これが望ましいです。

非正規化フロート値
フロートビット(指数仮数に署名)
最小の正(ゼロ以外)のフロート 0 00000000 00000000000000000000001
最小の負(ゼロ以外)のフロート 1 00000000 00000000000000000000001
最大の非正規化フロート 1 00000000 11111111111111111111111
正のゼロ 0 00000000 00000000000000000000000
負のゼロ 1 00000000 00000000000000000000000

露出したフロート

Javaフロートは、その内部の性質を明らかにします。以下のアプレットを使用すると、浮動小数点形式で遊ぶことができます。floatの値は、いくつかの形式で表示されます。基数2の科学的記数法の形式は、10進数で仮数と指数を示します。表示される前に、実際の仮数に2 24を掛けて整数を求め、不偏指数を24でデクリメントします。次に、仮数と指数の両方を10進数に簡単に変換して表示します。