単精度浮動小数点数

情報処理において、単精度浮動小数点数 （たんせいどふどうしょうすうてんすう、英: single-precision floating-point number）は、コンピュータの数値表現の一種である。 本来の単精度浮動小数点数とは、倍精度浮動小数点数に対比して使われる用語であって倍精度浮動小数点数が無いシステムであれば単に浮動小数点数と呼ばれるべきものである（また浮動小数点数はもともと固定小数点数と対比させた言葉であった）。 そうして、単精度はシステムの基本となる精度であり、バイトマシン（キャラクターマシン）が普通になる前のワードマシンにおいて1語長分の記憶場所を占める浮動小数点数であるのが普通である。 昔のFORTRAN言語では、整数型と実数型は同じ語長を占めることを前提に言語の規格が制定されていたりもした。その記憶語1単位分のを占める実数型が単精度であり、記憶語2単位分を占める実数型は倍精度と呼ばれた。 Crayのベクトルマシンのような語長が64ビットである科学技術専用計算機では、たとえば単精度とは64ビットを意味した。 現在の浮動小数点形式の標準であるIEEE 754では、単精度は32ビット（4オクテット）、倍精度は64ビット（8オクテット）である。IEEE 754-2008 では単精度の形式は正式には binary32 と呼ばれている。「単」や「倍」という表現は32ビットを1ワードとする32ビットアーキテクチャを基にしている。 単精度浮動小数点数は、同じ幅の固定小数点数に比べてより広範囲な数値を表せるようになっているが、同時に精度を犠牲にしている。 ほとんどのプログラミング言語では標準の組み込み型として定義されている。C言語、C++、C#、Java、Haskellでは単精度のデータ型を float と呼ぶが、C/C++の規格ではIEEE 754に準拠することは要求されていない。MATLABでは single と呼ぶ。PascalおよびVisual Basic .NETでは Single と呼ぶ。.NET Frameworkの共通型システムではSystem.Single構造体型として定義されており、各.NET言語における組み込み型はこの型へのエイリアスである。バージョン3.2以前のOctaveでは single という名前だが倍精度である[要出典]。Pythonの float は倍精度であり、単精度のサポートはない。F#の単精度は float32 または single であり、倍精度は float または double である。 IEEE 754 での binary32 の定義は次の通りである。 符号ビット: 1ビット 指数部の幅: 8ビット 仮数部の幅: 23ビット 符号ビットは表現する数値の符号（正負）を示す。指数部は 8 ビットで、符号なし整数として見た場合は 0 から 255 の値をとり、0 の時はゼロか非正規化数であることを、1 から 254 の時は -127 のゲタ履き（バイアス付き）表現で -126 から 127 の指数を、255 の時は無限大または NaN であることを示す。 指数部が 0（ゼロ、および非正規化数）の場合を除き、仮数部で表現されるビットパターンのさらにひとつ上のケタに暗黙の 1 のビットがあるとみなす表現法（いわゆる「ケチ表現」）により、通常の数（正規化された数）の精度は、24ビット相当である。従って十進に換算したとき表現できる桁数は log10(224) ≈ 7.225 桁となる。標準化されたレイアウトは以下のようになる。 符号ビットを sign、バイアスつきの指数部を e、23ビットの仮数部の各ビットを b-n とすると、32ビットの binary32 フォーマットで表される数値は ( − 1 ) sign ( 1. b − 1 b − 2 . . . b − 23 ) 2 × 2 e − 127 {\displaystyle (-1)^{\text{sign}}(1.b_{-1}b_{-2}...b_{-23})_{2}\times 2^{e-127}} となり、より正確に表現すると value = ( − 1 ) sign ( 1 + ∑ i = 1 23   b − i 2 − i ) × 2 e − 127 {\displaystyle {\text{value}}=(-1)^{\text{sign}}\left(1+\sum _{i=1}^{23}\ b_{-i}2^{-i}\right)\times 2^{e-127}} となる。 単精度バイナリ浮動小数点数の指数部はオフセット表現を使って符号化されており、指数値がゼロのときのオフセット値（バイアス値）は127である。 Emin = 01H−7FH = −126 Emax = FEH−7FH = 127 指数部バイアス = 7FH = 127 指数部バイアスは、エクセスNとも言う。詳しくは符号付数値表現を参照されたい。真の指数値は、指数部の値から指数部バイアスを引いた値となる。 00H と FFH は予約された指数値である。 従って、全てのビットパターンが符号として意味がある。正の最小値（非正規化数）は 2−149 ≈ 1.4 × 10−45 である。正の正規化数の最小値は 2−126 ≈ 1.18 × 10−38 である。表現可能な最大値は (2−2−23) × 2127 ≈ 3.4 × 1038 である。 IEEE754 では実数値から対応する binary32 フォーマットへの変換を丸めも含めて厳密に定めている。 ここでは十進の実数値から IEEE 754 binary32 フォーマットへの変換は大まかに次のようになる。 実数値を 12.375 のように整数部と小数部で表すものとする。 整数部を二進法で表現したものに変換する。 小数部を以下に示す技法で変換する。 2つの変換結果を組み合わせ、さらに変換を加えて最終的な表現を得る。 小数部の変換: 12.375 の小数部 0.375 を例とする。これを二進の小数に変換するため、小数部に2をかけて整数部をとり、さらに2をかけて整数部をとるというように繰り返していき、小数部がゼロになるか精度の上限である二進23桁になるまで続ける。 0.375 x 2 = 0.750 = 0 + 0.750 ⇒ b−1 = 0 となるので、二進での小数部の一桁目はゼロとなる。そこでさらに 0.750 に2をかける。 0.750 x 2 = 1.500 = 1 + 0.500 ⇒ b−2 = 1 0.500 x 2 = 1.000 = 1 + 0.000 ⇒ b−3 = 1 となり、小数部が 0.000 となったのでここで変換は停止する。 これで (0.375)10 は正確に二進で (0.011)2 で表されることがわかった。十進で有限桁数の小数が必ず二進で有限桁で表せるわけではない。例えば、十進の 0.1 は二進では正確に表現できないため近似値を用いることになる。 したがって、(12.375)10 = (12)10 + (0.375)10 = (1100)2 + (0.011)2 = (1100.011)2 となる。 IEEE 754 binary32 フォーマットでは、値を ( 1. x 1 x 2 . . . x 23 ) 2 × 2 e {\displaystyle (1.x_{1}x_{2}...x_{23})_{2}\times 2^{e}} という形式で表現しなければならないので、1100.011 をシフトして整数部が一桁になるようにしなければならない。この例では3桁シフトするので ( 1.100011 ) 2 × 2 3 {\displaystyle (1.100011)_{2}\times 2^{3}} となる。 最終的に ( 12.375 ) 10 = ( 1.100011 ) 2 × 2 3 {\displaystyle (12.375)_{10}=(1.100011)_{2}\times 2^{3}} となる。 この結果から次が得られる。 指数部は 3 となる（バイアスを加えるので実際は 130 = 1000 0010 となる）。 仮数部は 100011 となる（小数点より右側だけを仮数とする）。 したがって、IEEE 754 binary32 フォーマットで 12.375 を表すと 0-10000010-10001100000000000000000 = 41460000H となる。 注: 68.123 を IEEE 754 binary32 フォーマットで表す場合を考えてみよう。上述の技法を適用すると 42883EF9H というビット列が得られ、最後の4ビットは 1001 となる。しかし、IEEE 754 のデフォルトの丸め方式により最終的に 42883EFAH となり、最後の4ビットは 1010 となる。 例: 十進の1を変換すると ( 1 ) 10 = ( 1.0 ) 2 × 2 0 {\displaystyle (1)_{10}=(1.0)_{2}\times 2^{0}} となる。ここから次のことが導かれる。 指数は 0 である（バイアスを加えるので 127 = 0111 1111 となる）。 仮数は 0 である（小数点より右側はゼロなので仮数部のビット列は全てゼロになる）。 従って実数 1 を IEEE 754 binary32 フォーマットで表すと 0-01111111-00000000000000000000000 = 3f800000H となる。 例: 0.25 を変換すると、 ( 0.25 ) 10 = ( 1.0 ) 2 × 2 − 2 {\displaystyle (0.25)_{10}=(1.0)_{2}\times 2^{-2}} となる。ここから次のことが導かれる。 指数は -2 である（バイアスを加えるので 127+(−2)= 125 = 0111 1101 となる）。 仮数は 0 である（小数点より右側はゼロなので仮数部のビット列は全てゼロになる）。 従って実数 0.25 を IEEE 754 binary32 フォーマットで表すと 0-01111101-00000000000000000000000 = 3e800000H となる。 例: 0.375 を変換すると、 0.375 = ( 1.1 ) 2 × 2 − 2 {\displaystyle 0.375={(1.1)_{2}}\times 2^{-2}} となる。ここから次のことが導かれる。 指数は -2 である（バイアスを加えるので 127+(−2)= 125 = 0111 1101 となる）。 仮数は 1 である（1.1 の小数点より右側は 1 = x1 のみである）。 従って実数 0.375 を IEEE 754 binary32 フォーマットで表すと 0-01111101-10000000000000000000000 = 3ec00000H となる。 浮動小数点数値のビット列を十六進法で表した例を以下に示す。これには符号、バイアスを加えた指数値、仮数値が含まれている。 3f80 0000 = 1 c000 0000 = -2 7f7f ffff ≒ 3.4028234 x 1038 (単精度浮動小数点数の正の最大値) 0000 0000 = 0 8000 0000 = -0 7f80 0000 = 正の無限大 ff80 0000 = 負の無限大 3eaa aaab ≒ 1/3 1/3 は倍精度とは異なり、切り上げられる。これは仮数部のビット数が偶数であるため、丸める桁位置以降が 1010... となってその桁位置で 1/2 より大きくなるためである。 16進の値 41c80000 を例として変換を行う。これを二進で表すと次のようになる。 41c8 000016 = 0100 0001 1100 1000 0000 0000 0000 00002 これを、符号ビット、指数部、仮数部の3つに分割する。 符号ビット: 0 指数部: 1000 00112 = 8316 = 131 仮数部: 100 1000 0000 0000 0000 00002 = 48000016 ここで仮数部に暗黙の整数ビットを加える。 仮数: 1100 1000 0000 0000 0000 00002 = C8000016 指数部の値から127を引いて実際の指数値を得る。 指数部の値: 8316 = 131 本来の指数: 131 − 127 = 4 24ビットの仮数は最上位ビットが1に対応し、次の桁が0.5、その次が0.25というように、前の桁の1/2に対応している。 bit 23 = 1 bit 22 = 0.5 bit 21 = 0.25 bit 20 = 0.125 bit 19 = 0.0625 . . bit 0 = 0.00000011920928955078125 この例では、仮数で立っているビットは bit 23、bit 22、bit 19 の3ビットだけであり、上記の対応する値を加算することで十進での仮数が得られる。 本来の仮数: 1 + 0.5 + 0.0625 = 1.5625 = C80000/223 基数 2 を指数値でべき乗したものを仮数にかけると本来の値が得られる。 1.5625 × 24 = 25 従って 41c8 0000 = 25 となる。これは次の式と等価である。 n = ( − 1 ) s × ( m 2 − 23 ) × 2 x − 127 {\displaystyle n=(-1)^{s}\times (m2^{-23})\times 2^{x-127}} ここで s {\displaystyle s} は符号ビット、 x {\displaystyle x} は指数部、 m {\displaystyle m} は仮数を基数10で表したものである。 MSX-BASICの演算ルーチンMATHPACKの場合、同様に4バイトで表すが、IEEE 754とは異なり s(符号ビット): 1 y(指数部の幅): 7 x(仮数部の幅): 24 syyy yyyy xxxx xxxx xxxx xxxx xxxx xxxx であり、指数部をバイナリ、仮数部をBCDで表現する。 そのため、有効数字は正確に10進で6桁で、指数は±63乗である。 他のパソコンのBASICはほとんどが2進での演算であった（他にBCDを採用した機種としてFP-1000がある）。 2進小数の主な利点は高速性とメモリ消費にあり、BCD小数の主な利点は現実の10進小数を誤差なく扱える事にあったと言ってよい。 2進小数は原理上0.5や0.25、0.375といったn/(2^m)以外の値、つまり0.1や0.2といった数値を正確に扱う事が苦手であり(表示上は補正されているが、10進小数を2進小数に変換すると殆どの場合循環小数となり、微小な切り捨てが発生する)高速性よりも(10進の世界での)正確性が優先されるようなケースにおいてBCD小数は強みを発揮した(各種利息計算など)。 ただし当時のスペックにおいて10進小数は相応に重い処理であり、DAAという10進補正用の専用CPU命令を持っていたZ80であっても複雑な演算になればなるほど(例えば三角関数や対数関数のような数学関数)2進小数より負荷が増していく。 MSX-BASICにおいて10進小数が採用された理由は、マイクロソフトBASICの中でも比較的後期に開発され新しい実装を試せた事、加えてホームコンピュータという需要の開拓においてより一般性を高めたいアスキー側の狙いがあったと考えられる。 CPUの浮動小数点ユニットとの相互運用を視野に入れなければならない現代の言語では、特殊な用途を除いてBCD小数を目にする機会は少ない。