小数点を動かして広い範囲を表す
限られた記憶領域で小数を扱う方法には、固定小数点数と浮動小数点数があります。固定小数点数は、小数点の位置をあらかじめ決めておく方式です。たとえば小数点以下を2桁と決めれば、整数として記録した「1234」を「12.34」と解釈できます。計算の規則が分かりやすい一方、同じビット数では、極端に大きい数と極端に小さい数の両方を表すのが難しくなります。
浮動小数点数は、数をおおまかに「符号」「仮数」「指数」に分けます。符号は正負、仮数は有効な数字の並び、指数は桁の位置を表す部分です。十進法の科学的記数法で、6020を「6.02 × 10の3乗」と表す考え方に似ています。指数を変えると同じ桁数で表せる値の範囲が広がるため、非常に大きい値や非常に小さい値を扱う科学計算、画像処理、音声処理などに向いています。
実際のコンピュータでは、この仕組みを二進法で実現します。ただし、記録に使えるビット数は有限です。広い範囲を表せる代わりに、すべての実数を一つずつ正確に記録できるわけではありません。浮動小数点数では「表せる範囲」と「細かさ」の両方に限界がある、と捉えることが大切です。
0.1を二進法にすると有限桁で終わらない
十進法では、分母が2や5だけからなる分数は有限小数で表せます。たとえば2分の1は0.5、10分の1は0.1です。しかし二進法の各桁が表すのは、2分の1、4分の1、8分の1というような2の累乗分の1です。そのため、十進法の0.1は二進法では同じ並びが続く小数になり、有限個のビットだけでは正確に表せません。
そこでコンピュータは、表現可能な値のうち、目的の値に近いものへ丸めて保存します。プログラムに「0.1」と書いても、内部に保存される値は数学上の10分の1と完全に同じとは限りません。その近似値どうしで足し算などをすると、結果が期待した十進小数からわずかにずれることがあります。
これは入力や演算の失敗ではなく、有限のメモリで実数を近似する方式に伴う性質です。また、十進法で割り切れない3分の1を0.333…と近似するのと同じように、どの進法でも有限桁で表せない数はあります。二進法だけが不正確なのではなく、使う進法によって有限桁で表せる分数が異なります。
丸め・桁落ち・情報落ちは起こり方が違う
丸め誤差は、正確な値を限られた桁数へ収めるときに生じる差です。数を保存した時点だけでなく、演算結果を再び決められた桁数へ収めるときにも起こります。多数の計算を重ねると、個々には小さな差が結果へ影響する場合があります。ただし誤差は常に同じ向きへ増えるとは限らず、計算の順序や値によって現れ方が変わります。
桁落ちは、値がほぼ等しい二つの数を引いたときに目立ちます。先頭側の共通する桁が差し引かれ、もともと丸めの影響を受けていた下位の桁が結果の中心になるため、有効な情報が大きく減ります。式が数学的に正しくても、浮動小数点数での計算には不利な形があるという例です。
情報落ちは、桁の大きく異なる値を加減するときに起こります。大きい値に桁をそろえると、小さい値の有効な部分が保持できる桁の外へ押し出され、加えたはずの値が結果へ反映されないことがあります。さらに、表現可能な最大の大きさを超える現象をオーバーフロー、ゼロに近すぎて通常の精度では扱いにくくなる現象をアンダーフローと呼びます。いずれも、誤差と範囲の限界を意識すべき場面です。
「等しい」より許容範囲で判定する
計算結果を扱うときは、目的に応じて誤差を許容する設計が必要です。たとえば測定値やシミュレーション結果では、二つの値が完全に一致するかではなく、その差が定めた許容範囲内かを調べる方法があります。許容範囲は一律に決めるものではありません。扱う値の大きさ、必要な精度、誤差が結果に与える影響を考えて決めます。
計算式の変形や演算順序の見直しも有効です。ほぼ等しい値の引き算を避ける、非常に小さい値から順に加えるなど、同じ数学的結果を目指す式でも誤差の影響は変わります。これはアルゴリズムを選ぶとき、答えが理論上正しいかだけでなく、有限精度の計算で安定しているかを考える理由になります。
金額のように最小単位が決まっており、端数の扱いに明確な規則が必要なデータでは、最小単位の整数として持つ方法や、十進小数を扱うための型が使われます。浮動小数点数を避ければすべて解決するわけではなく、丸める時点や方法を決める必要は残ります。必要な範囲、精度、処理速度を踏まえて表現方法を選ぶことが重要です。
「浮動」しているのは点そのものではない
「浮動小数点」という名前から、メモリの中で小数点記号が左右へ移動しているように想像しがちです。しかし通常、小数点記号そのものを保存するわけではありません。仮数と指数を組み合わせ、指数によって桁の位置を解釈するため、小数点の位置を固定せずに扱えるという意味です。
また、表示された桁数と内部の精度は必ずしも同じではありません。画面には読みやすいように丸めた十進数が表示されても、内部では二進法の近似値が保持されています。反対に、内部の値を多くの十進桁で表示すると、入力時には見えなかった端数が現れる場合があります。計算は最終的にCPUなどによって処理されますが、表示の仕方と保存された値と数学上の値を区別すると、意外な計算結果の理由を整理しやすくなります。