浮動小数点演算での誤差のはなし

エクセルの仕事やってて、たまたま出てきた問題。
45.2 - 38.8 はいくつ?
常識的に考えたら、6.4 だよなあ。ところが。

vbaの答え

? 45.2-38.8
 6.40000000000001

なんじゃこりゃああああああああああああああああ!!!!コンピュータのクセに間違うとはナニゴト!!

他の処理系はどうなんだろうと思って調べてみた。

perlの答え http://codepad.org/OWDONYfj

$a=45.2;
$b=38.8;
print $a-$b
# 6.40000000000001

rubyの答え http://codepad.org/TqYa8sA2

$a=45.2;
$b=38.8;
print $a-$b
# 6.40000000000001

cの答え http://codepad.org/TKxjBYEB

int main() {
    double a;
    double b;
    double c;

    a = 45.2;
    b = 38.8;
    c = a - b;

    printf("%f", c);
    return 0;
}
/* 6.400000 (ビットがたらない) */

これは、浮動小数点演算の誤差ってやつで、IEEE 754の問題らしい。IEEE 754で定められた倍精度浮動小数点数を使っている処理系は答えがおかしくなるそうだ。今までは、整数値同士の計算だとか、小数点は切り捨て/切り上げで丸めちゃったりしてたから、あんまり気にも留めていなかったので、目から鱗だった。

参考URL→http://pc.nikkeibp.co.jp/pc21/special/gosa/eg4.shtml

単純な引き算でも、浮動小数点演算してると誤差の出る場合がある、と。おっかないっすね。

本当におっかないことが起こった事例もあるそうです。
参考URL→http://itpro.nikkeibp.co.jp/article/COLUMN/20071019/285010/?ST=oss&P=5

●事例1

 1991年2月25日,湾岸戦争においてアメリカ軍のパトリオット・ミサイルはイラク軍のミサイルの撃墜に失敗。ミサイルはアメリカ軍の兵舎に命中。28名の兵士が死亡した。失敗の原因はミサイルが起動してからの時間計算に誤差があったためだった。

●事例2

 1996年6月4日,欧州宇宙機構ESA)が打ち上げた無人のアリアン5型ロケットは発射後わずか40秒後に爆発した。アリアン5型ロケットの開発には10年近くの歳月と約70億ドルの費用がかかっていた。ロケットと搭載されていた器材の総額は約5億ドルにものぼった。事故の原因は慣性基準装置(IRS)内にあるソフトウエアが水平方向の速度を表現する64ビット浮動小数点数を16ビット整数に変換していたため。この数値は16ビット符号付き整数の最大値である32768を越えて変換は失敗,事故の直接の原因となった。

 このような深刻な事態はまれですが,それでなくても浮動小数点数は誤差による間違いを引き起こしやすいということを忘れないでください。

しゃれになんねー

ちなみに、pythonphpは勝手に丸めてくれるっぽい? 内部表現に違いがあるのかな?

python http://codepad.org/L5SHLZ9x

a=45.2;
b=38.8;
print a-b;
# 6.4

php http://codepad.org/qTpDeiwp

<?php
$a=45.2;
$b=38.8;
echo $a-$b;
// 6.4
?>

結論

コンピュータでも間違える。

いや、厳密に言えば間違いというか、「仕様」ってやつなんだろうけど。