2014年4月19日土曜日

コンピューターは小数の計算が苦手

桁処理の意外な結果

前回の面積表で、「小数点以下第6位」を切り捨てにしてみたところ、次のような結果になりました。
計算間違い?
7.12560000007.1256になるべきですが、答えは7.1255になっています。その次の値もおかしいですね。なぜこのようなことになるのでしょうか?

表示と本当の数字は違う

この面積、実は内部の値として

7.1255999999994㎡

の場合があります。これが丸め処理されて、表示値は「7.1256」となっていたというわけです。確かにこの値を小数点以下第6位で切捨てすれば、7.1255になるのは当然です。表示されている値はすでに「丸め」処理が行われた数字なのです。

しかし、なぜこのような端数が生じるのでしょうか?

コンピューターでは数字を2進数で保持します。ところがほとんどの小数は2進数で正確に表すことができません。これは0または1(=2進数)を基礎とした計算を基本とするコンピューターの宿命なのです。

エクセルでも誤差はある

エクセルを開いて、セルに

=0.3-0.2-0.1

と打ち込みます。当然「0」となります。ところが

=0.3-0.2-0.1-0

エクセルでも誤差が発生する

と打ち込むと、「-2.77556E-17」と表示されます。これが「誤差」です。皆さんが全幅の信頼を置いているエクセルでさえ、誤差は発生するのです。

コンピューターの倍精度浮動小数点と呼ばれる値には長年誤差が付きまとっています。金融界ではこのような誤差は致命的なので、この誤差を回避する様々な手法が開発されています。

誤差を回避するには

こうした誤差を回避するために、一般に丸め処理(=誤差修正)が行われていますが、これは表示上だけであり、実際に保持されている数字は変わらないので、このような問題が生じます。これを回避するにはどうすればいいでしょうか?方法は二つあります。

  • 丸め処理を複数回行う
  • 微小値を加える

前者の方法を用いて、前回の手法をすこし改造してみます。最初の面積を実数化した後に、面積値を一旦小数点以下第8位あたりで四捨五入し、表示値=内部保持値の状態を作り出したうえで、任意の桁で処理を行えばいいでしょう。

前回の面積表と同様、面積値を実数化した後に、小数点以下第8位で四捨五入するため10000000倍します。


計算式:(面積 / 1 m²) * 10000000
この時点で一部誤差が発生していることが確認できます。次にこれを「round(x)」関数を用いて四捨五入します。

計算式:round(実数×10000000)

結果を1000で除して、切捨て処理をします。

四捨五入÷1000の計算式:四捨五入/1000
切捨の計算式:rounddown(四捨五入÷1000)
最後に10000で割って、面積化の手順を踏めば誤差を修正した面積を取得できます。

切捨処理÷10000の計算式:切捨処理/10000
結果の計算式:切捨処理÷10000 * 1 m²

まとめると次のような式となります。
(rounddown(round((面積 / 1 m²) * 10000000) / 1000) / 10000) * 1 m²

誤差を修正

コンピューターの演算は有限桁数で行っているため、必ず誤差が発生します。

エクセル 小数 誤差

で検索してみると多数の事例や回避方法を見つけることができますのでご確認ください。

一般に皆さんがコンピューター上で見ている少数は、ほとんどすべての場合ある程度の桁で「丸め処理」が行われた表示上の数字です。コンピューターが内部に保持している値はもっと複雑な値であることをお分かりいただけたでしょうか?