普通の式で時たま現れる浮動小数値の誤差

小数値の計算では誤差が生じるのは知識として知っていても、あまり出会ったことがない。最近、遭遇したので記録しておく。

0.1を100回足したら10ではないとか、小さな数値と大きな数値を足したら桁落ちするとかではなく、極々普通の式で表面化する誤差の話。

3.5 + 37.2 + 27.2 + 29.2

4つの数値を足すと97.1なのだが、float型で計算すると「97.10001」になる。
            float v1 = 3.5F + 37.2F + 27.2F + 29.2F;
            System.Diagnostics.Debug.WriteLine(v1);    // 97.10001
これはdouble型で計算すれば97.1になるので、普通はあまり出会わない。
            double d1 = 3.5 + 37.2 + 27.2 + 29.2;
            System.Diagnostics.Debug.WriteLine(d1);  // 97.1

この「0.5+0.2+0.2+0.2」の式だが、float型で計算してもいつでも誤差が見えるわけではない。
            float f1 = 0.5F + 0.2F + 0.2F + 0.2F;
            System.Diagnostics.Debug.WriteLine(f1);    // 1.1

            float f2 = 10.5F + 10.2F + 10.2F + 10.2F;
            System.Diagnostics.Debug.WriteLine(f2);    // 41.1

            float f3 = 20.5F + 20.2F + 20.2F + 20.2F;
            System.Diagnostics.Debug.WriteLine(f3);    // 81.10001

            float f4 = 30.5F + 30.2F + 30.2F + 30.2F;
            System.Diagnostics.Debug.WriteLine(f4);   // 121.1
0.2を3回足すと誤差が生じているようなので、0.2だけを3回足してみた。
            float f1 = 0.2F + 0.2F + 0.2F;
            System.Diagnostics.Debug.WriteLine(f1);  // 0.6
            float f2 = 10.2F + 10.2F + 10.2F;
            System.Diagnostics.Debug.WriteLine(f2);  // 30.6
            float f3 =  20.2F + 20.2F + 20.2F;
            System.Diagnostics.Debug.WriteLine(f3);  // 60.6
            float f4 = 30.2F + 30.2F + 30.2F;
            System.Diagnostics.Debug.WriteLine(f4);  // 90.60001
            float f5 = 40.2F + 30.2F + 40.2F;
            System.Diagnostics.Debug.WriteLine(f5);  // 110.6
デバッガで実際の数値を見てみると、以下のようになっていた。f1を除いてみな誤差がありまくりだった。しかし表面化しているのはf4だけ。小数点以下6桁目を四捨五入しているのだろうか。でもそれならf5も誤差が表面化するはず。数値として9桁で8桁目を四捨五入している感じ?


40.2 - 40.1

この式は0.1だが、double型で計算しても誤差が表面化してしまう。結果は「0.100000000000001」となる。
            double d1 = 40.2 - 40.1;
            System.Diagnostics.Debug.WriteLine(d1);  // 0.100000000000001

なかなか、やっかい。

エクセルでの計算でも

(2018/5/12追記)エクセルで0.1違いの値を引き算した結果を、小数点以下を長く表示するように書式指定してみると0.0999999999999996 と表示される。


まあ、こういうものなのか。




コメント

このブログの人気の投稿

varchar をデータ型 numeric に変換中に、算術オーバーフロー エラーが発生しました。