分かったようでよく分からない浮動小数点演算の誤差 with Excel
Introduction
日々の生活や仕事の中で得られたことを次に活かせるようなOutputにまとめる、とこのブログを始めたときに宣ったものの、遅々として進んでいませんね。。。なので今回は気を取り直して、最近 お目にかかった浮動小数点誤差についてまとめてみようと思います。この言葉自体、知ってはいたものの、ちゃんと理解していたわけではなく人にうまく説明もできず、浮動小数点の誤差だから仕方がないで済ませ気味の自分にそろそろ嫌気がさし。改めて調べてみましたが、やっぱり分かったようでよく分からないところも。。。
というわけで、浮動小数点演算の誤差について、自分の理解内容をもとに分かる範囲で分かりやすさ優先でまとめた見ました。拙い理解のもと書いているので、数学的・情報工学的に正確でない箇所が含まれている可能性がある旨、ご了承くださいませ。
What's Happened??
ことの発端は、本業のシステム導入にてExcelからのインポート機能を使用してデータをアプリケーションに取り込もうとした際に、Excel上の値とアプリケーション上に取り込まれた値が異なるというもの。Excel上では 0.1 だったものが、アプリケーション上は 0.0999999999999999 と表示されたというもの。この時点で浮動小数点演算のあるあるだな、と察しはしました。
よくある例をもとにこの問題を見てみましょう。Excel上で下記のように 1.4 - 1.3 を計算します。一見 0.1という結果が得られます。
A | B | C | D | E | |
---|---|---|---|---|---|
1 | 1.4 | -1.3 | = A1 + B1 |
↓
A | B | C | D | E | |
---|---|---|---|---|---|
1 | 1.4 | -1.3 | 0.1 |
次に、D1セルに直接0.1と入力し、E1上でC1とD1の比較結果を出してみましょう。同じ0.1同士の比較なのでTRUEが期待されますが、結果はFALSE 不一致になる挙動をみせます。
A | B | C | D | E | |
---|---|---|---|---|---|
1 | 1.4 | -1.3 | = A1 + B1 | 0.1 | = C1=E1 |
↓
A | B | C | D | E | |
---|---|---|---|---|---|
1 | 1.4 | -1.3 | 0.1 | 0.1 | FALSE |
この挙動が、浮動小数点演算を起因としたものになります。 実はC1の演算結果はExcelの表示上は0.1になっていますが、実際には0.1ではないのです。C1セルに対して「小数点以下の表示桁数を増やす」を繰り返していくとあるタイミングで、0.0999999999999999 と表示されると思います。本当の計算結果はこれであり、0.1 は見栄えをよくしたかりそめの姿だったので。
A | B | C | D | E | |
---|---|---|---|---|---|
1 | 1.4 | -1.3 | 0.0999999999999999 | 0.1 | FALSE |
故に、最初に述べたようなアプリケーションに取り込んでみると0.1ではない値が表示されるという事象が発生しました。では、なぜこんなことが起きるのか。浮動小数点演算の誤差とは何者なのか?
そのことをユーザにうまく説明できず、結局はアプリケーションの開発元に問い合わせして回答をもってその場をおさめるという逃げの一手にでてしまいました。情けない。
ということで今回、ちゃんと調べて理解を試みました。
ざっくりした結論
先に結論をざっくり書いてみます。
- コンピューターは小数の扱いが得意でなく、全ての小数を正確に表現することができない
- 一定の精度を保ちつつ現実的なリソースの範囲内で近似的に小数演算するための規格がIEEEによって制定され(IEEE754)、現代のコンピューターはそれに則り実装されている
- それ故にExcelを含めたコンピューター上での小数演算は近似値的な計算となり、期待した結果にならないことがある
- Excelは、表に見えている表示上の表現と、裏に持っている実際の演算結果が必ずしも一致しない、表示上は見えやすい形に丸めているから
- そのため、Excelの計算結果をアプリケーション等のプログラムに取り込むと、計算結果がExcel画面上と違って見えることがある
2進数での小数表現方法
ご存じのとおり、現代のディジタルなコンピューター上では、数字は0と1の2進数で表現される。具体例を見てみよう。
2進数表現 | 2進数 → 10進数変換 | 10進数表現 |
---|---|---|
111 |
| 7 |
110 |
| 6 |
101 |
| 5 |
100 |
| 4 |
11 |
| 3 |
10 |
| 2 |
1 |
| 1 |
0 |
| 0 |
0.1 |
| 0.5 |
0.1001 |
| 0.5625 |
0.1010 |
| 0.625 |
0.1011 |
| 0.6875 |
0.11 |
| 0.75 |
0.1101 |
| 0.8125 |
0.111 |
| 0.875 |
0.1111 |
| 0.9375 |
この例からわかるように整数値は10進数表現の順に(1,2,3,4,5...)2進数でも表現できるが、小数値は整数値のような綺麗な並びになっていないことがわかる。2進数で広い範囲の小数値を表現するには、小数点以下の桁数を増やして必要があるが、コンピューターのリソースには限界がある。また無限小数と呼ばれる演算結果が循環し無限の桁数をもっても2進数では表現できない10進数の小数も存在する(例えば0.1は2進数では無限の桁数を持っても表現することができず、0.1に極めて近い近似値で代用されている)。
このように
- 広い範囲の小数値を2進数で表現するためには、小数点以下に巨大な桁数が必要になる
- 無限の桁数を持っても表現しきれない小数値が存在する
ことが、コンピューターが小数の扱いを得意としない理由にあり、小数点演算誤差の原因の根っことなる。
浮動小数点にかかわる誤差
上記のような2進数の小数を表現する際に、値によって桁数が変動してしまうが、これはコンピューター上では扱いにくい。そこでコンピューター上では、2進数の小数を、
- 符号 仮数 * 基数^指数
という形の指数表記する。具体的には 0.1001 であれば、
+ 1.001 * 2^-1
のようになる。このような表現によって整数桁数・小数桁数を固定して表現できるようにしている(この例であれば整数1桁、小数3桁)。先の例にも当てはめるとこうなる。
2進数表現 | 指数表現 |
---|---|
0.1 |
|
0.1001 |
|
0.1010 |
|
0.1011 |
|
0.11 |
|
0.1101 |
|
0.111 |
|
0.1111 |
|
このように小数点の位置を移動させることで決められた桁数の範囲内であっても幅広い小数値を表現可能にする仕組みを浮動小数点と呼んでいる。
一方でこの表現方法にも誤差を生み出す要因が存在する。主な誤差について記載する
丸め誤差・打切り誤差
浮動小数点は、事前に桁数の範囲が決められているため、小数点の位置のスライドだけでは表現できない範囲の切り捨てが発生する。例えば次の2つの2進数小数を整数1桁、小数3桁で表現してみる。
- 0.00001
- 0.10011
1つ目の0.00001 は、小数点を5つ動かし、1.000 * 2-^5 と表現することができる。一方の0.10011は小数点以下に数字が5桁以上存在し小数点をスライドしたとしても整数1桁、小数3桁の範囲内では表現できない。結果 最下位桁の1が切り捨てられ 1.001 * 2-1 と表現せざるを得なくなる。このように、表現しきれない桁の切り捨てによって発生する計算誤差は、丸め誤差と呼ばれる。
丸め誤差に似たものとして打切り誤差が存在する。これは無限の桁数を持っても表現しきれない無限小数について、規定された桁数まで計算したのち、後続の桁の計算を打ち切り丸めたことが発生するものである。
代表的なものとして、10進数の0.1は、2進数では0.000110011001100110011001... と1001が永遠に繰り返される無限小数だが、 今回の例のように整数1桁、小数3桁内で表現しようとすると、0.0001100 → 1.100 * 2^-4 となる。この2進数を10進数に戻すと0.09375 と打ち切った分 ズレが発生していることがわかる。これが打切り誤差である。
情報落ち誤差・桁落ち誤差
大きな値と小さい値の加減算をする際、小さい側の値が桁数調整の結果消失し、無視されてしまう結果発生する誤差を、情報落ち誤差呼ぶ。
限りなく近い値の小数同士で引き算をした際、仮数武の有効桁数が減って情報を消失することで発生する誤差を、桁落ち誤差と呼ぶ。
具体例は。。。後々記載します。。。
Excelの面倒・お節介の点
このようにコンピューター上での小数、すなわち浮動小数点で表現された2進数の小数値の演算には、様々な誤差の要因が含まれるため、簡単な演算であっても、机上での結果・期待値と一致しないケースが珍しくない。最初に述べたExcel上での 1.4-1.3 がその典型例である。
一方で今回、話をより複雑にした要因に、Excelの面倒かつお節介な仕様も含まれている。というのもExcelが1.4-1.3の計算結果をそのまま表示してくれていればよかった。にもかかわらず表面上は 0.1 という直感上 正しいが実際の計算結果とは一致しない値を出力したがために、見る人を混乱させた。
これ自体はExcelの仕様であり、計算結果が無限小数になってしまったがために、表示上は打切りがされた結果、0.1 と出力されてしまったわけである。ただし実態としては0.1とは等しくない、0.1に限りなく近い小数値であるために、後続でおかしなことが発生していた。本当にExcelは便利なようで余計なお節介、忖度がトラブルを引き起こすことの多いツールだね。。。
終わりに
今回は、浮動小数点演算の誤差について、コンピューター上での小数表現方法とそれに起因する誤差についてまとめてみました。また、その誤差を忖度して表に出さないExcelの仕様について愚痴ってみました。一部分かりにくい部分や書きかけ・情報が不足している部分、誤っている・誤りを誘発しそうな部分もあると思うので、必要に応じて情報をupdateしていこうと思います。
でも、こうやってまとめてみたものの。。。やっぱり分かったようでよく分からないのよね、この問題。
ちなみに、Excel上での問題を回避する方法はいくつかあり、下記動画等でも解説されているので参考までに。
Comments