IEEE 754
IEEE 754(アイトリプルイーななごおよん、アイトリプルイーななひゃくごじゅうよん)は、その標記 IEEE Standard for Floating-Point Arithmetic のとおり、IEEE標準のひとつであり、浮動小数点算術に関する標準である。GNU coreutilsのマニュアルで「Almost all modern systems use IEEE-754 floating point」と書かれているように、ほぼ全てのmodernなシステムが使っている浮動小数点方式(の仕様)であり、多くの、プロセッサ内部のあるいは独立の、FPUなどといったハードウェア、あるいは浮動小数点演算ライブラリといったコンピュータ・プログラムが採用している。なお、多くのコンピュータ・プログラミング言語ないしその処理系の仕様では、IEEE 754 による処理とは明記していないことが多い。これは、異なる仕様を採用しているハードウェア上に実装する際のコストへの考慮のためである。ただしそういった言語や処理系でも、実機のシステムがIEEE 754準拠であれば、結果としてはIEEE 754準拠になる。あまり多くはないが、JavaやC#のように、言語仕様で IEEE 754 を明記しているものもある(ただしそのような仕様を厳密に実装するのはそう簡単ではない場合もある)。
21世紀になってから改定として2008年8月に制定された IEEE 754-2008 がある。これには、1985年の IEEE 754 制定当初の規格である IEEE 754-1985、ならびに基数非依存の浮動小数点演算の標準規格 IEEE 854-1987 の両者がほぼすべて吸収されている。IEEE 754-2008 は、正式に制定されるまでは、IEEE 754rと呼ばれた。
正式な規格名は、IEEE Standard for Floating-Point Arithmetic (ANSI/IEEE Std 754-2008)である。ISO/IEEEのPSDO(パートナー標準化機関)合意文書に基づき、JTC1/SC 25 を通して国際規格 ISO/IEC/IEEE 60559:2011 として採用され、公表されている。
この標準規格は、以下のようなことを定義している。
基本形式: 次の交換形式とは独立して抽象的な形で表現法を定めたもの。二進の他に十進形式もある。通常の「浮動小数点方式による数」の他、0、負の0、非正規化数、正の無限と負の無限(拡大実数)、0.0 / 0.0 の結果のような「数ではない」状態を表現する NaN、についての表現がある。
交換形式: ビット列としての表現形式であり、ファイルや通信などによる交換の際に機種に依存しない表現形式として定めたもの。
丸めとその規則: 端数処理の仕方に関する種類と規定。
例外的な処理: 例外的状態の扱い(ゼロ除算、オーバーフロー、など)。
四則演算: 四則演算は、両辺の値が正確にその値であるものとして数学的に正確に計算された値から、「指定された丸め」によって丸められなければならない。
その他の演算: その他の関数では、四則演算と同様の正確さを実現することは難しい場合がある。
またこの規格では、高度な例外処理、追加的な演算(三角関数など)、式評価、再現可能性などを強く推奨している。
IEEE 754-1985 から IEEE 754-2008 への改訂作業には、7年間かかった。改訂作業は Dan Zuras が指揮し、Mike Cowlishaw が編集責任者となった。IEEE 754-1985 にあった二進形式(単精度・倍精度)は、そのまま IEEE 754-2008 にも含まれている。さらに、IEEE 754-2008 では、新たに二進形式1つ、十進形式2つが加わり、計5つの基本形式が存在する。IEEE 754-2008 に「従っている」と主張する実装は、これらのうち少なくとも1つの基本形式を算術演算と情報交換のために実装しなければならない、とされている。
IEEE 754において、浮動小数点数データを取り扱うための符号化が形式 (Formats) として定められている。その中では次のデータを表現できる。
2または10を基数とする有限数。各有限数は、符号s(0または1)、仮数c、指数qの3つの整数で表現し、(−1)s × c × bqという値を意味する。bは基数で2または10である。例えば、符号が1(負数を意味する)、仮数が12345、指数が−3で、基数が10だった場合、−12.345という値を表す。
0(+0と−0)
+∞と−∞。
2種類の非数 (NaN)。NaNにはクワイエットNaN (Quiet NaN) とシグナリングNaN (Signaling NaN) が存在する。どちらのNaNでも、追加情報を伝えられる余分のビットが設けられている。
所定の形式で表現可能な有限数値は、基数 (b)、仮数の桁数すなわち精度 (p)、指数に関するパラメータ emax によって決定される。
cは0からbp−1までの値でなければならない(例えばb = 10 かつ p = 7 ならば c は0から9999999の範囲を取る)。
qは1−emax ≤ q+p−1 ≤ emaxでなければならない(同様に、例えばp = 7 かつ emax = 96 ならばqは−101から90の範囲を取る)。
括弧内の例のパラメータを用いた場合、0以外で最も小さい値は1×10−101と表現され、最も大きい値は9999999×1090 (または9.999999×1096) と表現される。このうち、仮数の桁数が充分確保されているのは1000000×10−101(または1.000000×10−95) 以上の値であり、これらの数は正規数と呼ばれる。一方、正負の最も0に近い正規数である−1×10−95と1×10−95に挟まれた区間は、非正規数として表現される。例えば、1×10−101はこの例において表現できる最も0に近い正の値だが、仮数の桁数が1しかなく非正規数である。
ゼロは仮数が0の有限数である。符号が別に定義されているので、符号付の2種類のゼロ +0 と−0 が存在する。
IEEE 754標準では、5種類の基本形式を定めており、基数や符号化して使用するビット数に応じて名前が付けられている。その内訳は、32/64/128ビットで表現する3種類の二進浮動小数点形式と64/128ビットで表現する2種類の十進浮動小数点形式からなる。このうち、二進形式の初めの2種は IEEE 754-1985 で単精度 (single)・倍精度 (double) と呼ばれた形式である。3つ目の二進形式は、四倍精度 (quad) とも呼ばれる。同様に、十進形式の2種も倍精度・四倍精度と呼ばれる。
基本二進形式の典型的な精度は実際に仮数部に保持されているビット数よりも1ビット分だけ高い。これは二進形式の正規化された浮動小数点数では最上位ビット(桁)が常に1であることを利用して符号化の際にそれを省いて表現しているからである(ケチ表現)。
十進換算の桁数は p × log10 b で得られる十進での桁数の近似値である。
十進換算の emax は emax × log10 b で得られる十進での指数最大値である。
算術その他の演算で使用される形式は、符号化したときのものと一致していなくても構わない(つまり、実装は、内部で異なる表現を使用してもよい)。
この標準では、精度(有効桁数)を拡張した形式を示し、基本形式よりも精度を高くすることを推奨している。拡張精度 (extended precision) 形式は、仮数部と指数部の桁数(ビット数)を基本形式よりも大きくしたものである。拡張可能精度 (extendable precision) 形式では、ユーザーが仮数部の桁数と指数部の範囲を指定できる。これらを内部の表現として実装することができる。ただし、そのような内部形式も、有限数の表現できる集合が定められるようパラメータ (b, p, emax) はきちんと定義されている必要がある。
また、この標準は拡張(可能)精度形式の実装を要求してはいない。
この標準では、言語がサポートしているそれぞれの基数 b について p と emax を指定する方法を提供することを推奨している。
また、拡張形式をサポートする場合は、サポートしているそれぞれの基数 b について最大の基本形式よりも高い精度をサポートすることを推奨している。
2つの基本形式の中間の精度の拡張形式の場合、大きい方の基本形式と同じかそれ以上の指数範囲を持たなければならない。したがって例えば64ビットの拡張精度二進形式なら、emax は16383以上でなければならない。Intel 8087 の80ビット拡張形式はこの要求を満たしている(binary128と指数の範囲が等しい)。
交換形式は固定長のビット列での浮動小数点数の交換や記録を意図した形式である。
二進浮動小数点数用に、16/32/64ビットにくわえて32の倍数で128ビット以上の交換形式が定義されている。16ビット形式(半精度)は、グラフィック用途など小さな値の交換または記憶に用いることが想定されている。
二進交換形式の符号化方式は IEEE 754-1985 と同じである。先頭に符号ビット1ビットがあり、それに w ビットの「バイアス」された指数部が続き、さらに p−1 ビットの仮数部が続く。全体で k ビットの形式の場合、指数部のビット数は w = floor(4 log2(k))−13 で得られる。ただし、64ビットと128ビットの形式ではこれで正しいが、16ビットと32ビットの形式では指数部にこの式で得られる値(それぞれ3と7)よりも多いビット(それぞれ5と8)が割り当てられている。
Wビットの幅を持つワードがある場合、整数でビットに番号を振る。その番号の範囲は0 − W−1 であり、0番のビットが右端となる。0番のビットは一般にLSBである。
単精度二進化浮動小数点数は、32ビットワードに格納される。
<tt>sign</tt> は符号、<tt>exponent</tt> は指数部、<tt>fraction</tt> は仮数部である。
指数部は下駄履き表現(バイアスまたはエクセスとも。符号付数値表現を参照)と呼ばれる形式であり、実際の値に、ある固定値(ここでは emax = 127)を加算したものである。このような表現にしているのは浮動小数点数同士の大小比較を容易にするためである。指数部は大きな値も小さな値も表せるように負の値にもなるが、これを単に2の補数で表すと、全体の符号 <tt>sign</tt> とは別に <tt>exponent</tt> も符号を持つことになり、単純な大小比較ができなくなってしまう。そのため、指数部はバイアスされて常に正の値となるような形式で格納される。単精度では−126 ~ +127に127を加えて、1 ~ 254としている(0と255は特殊な意味を持つ。後述)。この表現により「指数が正の数」「指数が1の数」「指数が負の数」「0」を、この順に自然に並べることができる。浮動小数点数を解釈するときは、バイアスを減算して実際の指数を求める。
表現可能なデータは指数部の値によって区別され、仮数部の値にも影響される。指数部も仮数部も符号無しの二進整数であることに注意されたい(指数部は 0 – 255)。
最も一般的な正規化数では、exponent はバイアスされた指数であり、fraction は仮数の小数点以下の部分である。先ほどの(−1)s × c × bqと対応づけると次のようになる。
s = sign
q = exponent − emax (ここではemax = 127であり、換言すれば、指数に127を加算して格納されている。「127でバイアス」しているとも言う)
b = 2
c = 1.fraction
正規数においてcは1以上2未満のため、常に1.xxx…と表記できる。このため、fractionにはxxx…の部分のみを格納し、実質1ビット多い24ビット精度を実現している。これはけち表現と呼ばれる。
なお、1 − emax = −126 が単精度における正規化数の最小の指数である。
非正規化数の場合q = −126で、cが 0.fraction とする。(qは−127 ではない。仮数の小数点以上の部分が0になっている関係で、指数を−126としてバランスをとっている。)
ゼロは二種類存在する。+0(sが0)と−0(sが1)である。
無限大も二種類存在する。+∞(sが0)と−∞(sが1)である。
NaNにも符号や仮数があるが、分析以外の目的では使えない。fraction の先頭ビットで 「signaling NaN」と「quiet NaN」を区別する。
NaNと無限大はexponentフィールドが全て1である。
−118.625(十進法)をIEEE 754 単精度で表現してみよう。
まず、符号と指数と仮数に分割する必要がある。
負の数なので、符号は 1 となる。
次に、絶対値を二進法で書くと、1110110.101となる(二進記数法を参照されたい)。
小数点を左に移動させ、1だけを左に残す。1110110.101=1.110110101×26となる。これが正規化された浮動小数点数である。
fractionは小数点の右側だけであり、足りないビット数のぶんだけ 0 で埋め 23ビットにする。結果は 11011010100000000000000 である。
指数は6であるが、バイアスを加える必要がある。32ビット IEEE 754 形式では、バイアスは127なので6 + 127 = 133となる。二進法に変換すると10000101である。
この結果をまとめると以下のようになる。
倍精度も、各フィールドの幅が広くなっているだけで、考え方は同じである。
正規化数では、指数はemax = +1023でバイアスされる(したがってeは exponent − 1023)。正規化数の指数は e: +1023 〜 −1022(exponent: 2046 〜 1)である(exponent = 2047 の時無限大またはNaN、exponent = 0 の時非正規化数で e = −1022)。正規化数の時仮数部はけち表現である。
半精度の交換形式は次のようになる。
また、四倍精度の交換形式は次のようになる。
浮動小数点数の比較は浮動小数点命令を使うのが最良である。しかし、形式やエンディアンや符号が同じであれば、数値をビット列としてバイト単位に比較することができる(ただし非数NaNは除く)。
ふたつの正の数値aとbについてa < bという比較を行う場合、これを符号無しの整数と見なして比較しても同じ結果を得られるようになっている。言い換えると、(NaN以外の)2つの正の浮動小数点数は符号無し整数として比較できる(ただし、エンディアンが異なる場合には比較できないので、移植性を問題とするコードで共用体を使ってこれを行うことは推奨できない)。これは辞書式順序の例である。
十進浮動小数点数に対しては、32の倍数のビット数での交換形式が定義されている。
二進の場合と同様、符号、指数、仮数と符号化していくが、仮数部は十進の各桁をより詰め込めるよう、BCDなどではなく Densely Packed Decimal および Binary Integer Decimal を採用し、ビットの扱いが二進と比べ複雑になっている。またこの標準では、2つの符号化方式(DPDとBID)のどちらを使っているかを示す方法を用意していない(世の中にはエンディアンが2種類あるが、IEEE 754 が交換形式のエンディアンを指定していないのと同じである)。いずれにせよ符号・指数・仮数によって表現することには変わりはない。また、NaNの表現は、二進同様に正規化数とは別扱いである。
IEEE 754-2008標準では5種類の丸めアルゴリズムが定義されている。うち2種類は最近接な値に丸める方法であり、残り3種類は方向丸めと呼ぶ。
最も近くの表現できる値へ丸める。表現可能な2つの値の中間の値であったら、一番低い仮数ビット(桁)が0になるほうを採用する。これは二進での標準動作かつ十進でも推奨となっている。
最近接丸め(0から遠いほうへ)
最も近くの表現できる値へ丸める。表現可能な2つの値の中間の値であったら、正の値ならより大きいほう、負の値ならより小さいほうの値を採用する。
0に近い側へ丸める。切り捨て (truncation) とも呼ばれる。
+∞への丸め
正の無限大に近い側へ丸める。切り上げ (rounding up, ceiling) とも呼ばれる。
−∞への丸め
負の無限大に近い側へ丸める。切り下げ (rounding down, floor) とも呼ばれる。
実装には、サポートしている(基本形式を含む)算術形式に対して次の演算が要求される。
算術演算(加減乗除・平方根・積和算・剰余・その他)
変換(複数形式間・文字列との相互・その他)
符号の複製・操作(絶対値・符号反転・その他)
比較・全順序
NaNその他の分類・判定
この標準では totalOrder という述語を提供しており、それぞれの形式におけるあらゆる浮動小数点数の全順序を定義している。通常の大小比較の演算子で大小が決まる場合は、この述語も同じ結果になる。しかし、比較演算子ではNaNとの比較が判定できず、−0 と +0 は等しいという結果になる。totalOrder はそういった場合でも大小を判定し、複数の NaN や同じ数値を異なる符号化方式で表現した十進形式間でも大小を区別する。
IEEE 754-2008では5種類の例外が定義されている。それぞれ、(確定的なアンダーフローを除いて)対応する状態フラグが存在し、例外発生時には対応するフラグが設定される。それ以外の動作は定義されていないが、デフォルト(下記)以外の追加の対処が推奨されている(後述)。
5種類の内訳は以下のとおりである。
無効な演算(負数に対して平方根を求めようとしたなど) - デフォルトではqNaNを返す。
0除算(1/0 や log(0) など) - デフォルトでは ±∞ を返す。
オーバーフロー(結果が正しく表現できないほど大きくなった場合) - 最近接丸めモードの場合、デフォルトでは ±∞ を返す。
アンダーフロー(結果が正規数で表現できないほどに小さく非0であるが不正確な結果となった場合) - デフォルトでは非正規化数を返す。
不正確 - デフォルトでは指定されたモードの丸めを施した結果を返す。
これらは、IEEE 754-1985と同一である。ただし、IEEE 754-1985 ではゼロ除算例外は除算のみだったが、IEEE 754-2008 ではそれ以外の演算でも発生する。
規格では、様々な形の例外処理をオプションとして推奨している。例えば、ユーザー定義のデフォルト値を事前に代入しておく方式、トラップ方式(何らかの方式で例外をとらえて制御フローを変化させる)、try/catch などの制御構造を使って例外を処理する方式などである。トラップや例外処理用制御構造はオプションである。
標準の9章では50種類の演算、対数、冪乗、三角関数などを言語標準で定義すべきだと推奨している。これらは全てオプションであり、標準に準拠するのに必須とされているわけではない。他にも丸めモードへのアクセスと設定の手段、ドット積などの各種ベクトル演算などが含まれている。実装にあたっては、その時点の丸めモードにしたがって正しく丸めた結果を返さなければならない。不正確例外を発生する場合はその限りではないが、他の例外が発生する場合でも丸め処理は正しく行う必要がある。
この標準では、一連の演算の並びの意味論を言語標準で提供することを推奨しており、式の文字通りの意味の微妙さと最適化が計算結果に与える影響を指摘している。この観点は以前の IEEE 754-1985 では全く触れられておらず、結果としてコンパイラ毎に計算結果が違ってきたり、同じコンパイラでも最適化レベルによって計算結果が異なるという状況を招いていた。
プログラミング言語は、それぞれの基数について、式を計算するときの途中の最小精度をユーザーが指定できるようにすべきである。これを標準では "preferredWidth" と呼び、プログラムのブロック毎に設定可能にすべきだとしている。式の計算途中で一時変数に途中結果を保存するとき、オペランドの最大幅を使い、設定されていれば "preferredWidth" を使うべきである。したがって例えば、x87 を対象とするコンパイラは計算途中の結果を拡張倍精度で保持するよう指定可能であることが望ましい。
IEEE 754-1985 では実装の自由度が大きかった(符号化や例外発生条件など)。IEEE 754-2008 では実装の自由度を狭めているが、それでも若干の自由は残っている(特に二進形式)。再現性に関する節では、再現性のあるプログラムが書けるよう言語標準に推奨しており(すなわち、その言語のどの処理系でも1つのプログラムが同じ結果となること)、そのためにどうすべきかを解説している。
この標準では、基本形式と「外部文字列」形式との間での変換機能を要求している。十進数の文字形式との変換は全形式について要求されている。外部文字列形式に変換したものを元の内部の形式に再変換したとき、全く同じ数値にならなければならない。NaNのペイロードを保持するという要求はなく、外部文字列形式に変換し数値に再変換したとき、signaling NaN が quiet NaN になることはありうる。
内部二進形式の値を十進の外部文字列形式に変換する場合、以下の十進有効桁数(以上)にすれば再びそれを内部二進形式に戻した場合に元の内部表現の値を完全に回復することができる。
binary16 の場合、5桁
binary32 の場合、9桁
binary64 の場合、17桁
binary128 の場合、36桁
これら以外の二進形式の場合、必要な桁数は次の式で計算できる。
1 + ceiling(p×log102)
ここで、 p はその二進形式の仮数のビット数で、例えば binary32 なら24ビットである。
なお、実装上の限界として、二進形式と外部文字列形式との間の変換で正しい丸め結果を保証できない場合がありうる。この標準では、最低でも上記の最小桁数より3桁多い桁数までは正しい丸めを保証せねばならず(shall)、桁数無制限に正しい丸めを保証すべきである(should)。
内部十進浮動小数点形式の場合には、外部文字列表現に於いて以下の桁数(以上)を使えば、それを再び読み込んで内部十進形式に戻した場合に、元の数値を完全に回復することができる。
decimal32 の場合、7桁
decimal64 の場合、16桁
decimal128 の場合、34桁
この標準では、十進形式と外部文字列表現の間での変換はいかなる桁数であっても正しい丸めを保証せねばならない。
二進と十進の間で正しく丸めを行いつつ変換するアルゴリズムが議論されており、評価されている。