C#でのDoubleの計算

C#からJavaに触れてつまづきそうになること – Challenge Java EE !.

上のサイトにある、doubleの処理。

doubleの計算

C#
Console.WriteLine(2.00 – 1.10);

0.9

Java
System.out.println(2.00 – 1.10);

0.8999999999999999

そんな馬鹿な!!

IEEE 754的には0.9という答えは実際にはおかしいということは皆さん理解していると思います。実際のところどうなのって言うことで、自分でもコードを書いて見ました。(本当に0.9って計算されていたらヤヴァイ)

using System;

namespace CsDoubleCalc
{
    class Program
    {
        static void Main(string[] args) {
            Console.WriteLine(2.0 - 1.1);
            Console.Read();

        }
    }
}

結果:

> 0.9

うーん。ということで、ILDASMでILを見てみます。

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // コード サイズ       23 (0x17)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldc.r8     0.89999999999999991
  IL_000a:  call       void [mscorlib]System.Console::WriteLine(float64)
  IL_000f:  nop
  IL_0010:  call       int32 [mscorlib]System.Console::Read()
  IL_0015:  pop
  IL_0016:  ret
} // end of method Program::Main

そもそも実際には計算してないで、コンパイルが計算結果をリテラルとして埋め込んでいるわけですが、ただ内部的にはIEEE 754通り計算していそうなことはわかったと思います。

つまり、計算結果に違いがあると言うより、.NET Frameworkコンソール出力ライブラリ(System.Console)が、人間的に文句が出ない方向で値をまるめているって言うのが正解だと思います。(それはそれでいいのかっていうのもあるけど・・・)

これで一安心です。

7 thoughts on “C#でのDoubleの計算”

  1. それで出力は同じなのに比較がFalseになってすごい悩んだことがありました。

    ConsoleというかToString()の標準のフォーマットだと丸められてしまうせいで、ToString(“R”)という風にラウンドトリップ指定するとちゃんと戻りますね。

  2. 記事のフォローありがとうございます。

    浮動小数点は統合で比較しちゃダメとか、基本的な部分が最近はまり共有できていないと思ったりすることが最近多いですね。

  3. Double型の書式設定(.ToString(string format))の挙動が原因であるようです。
    http://msdn.microsoft.com/ja-jp/library/kfsatb94(v=vs.80).aspx

    上記例であればConsole.WriteLine(“{0:G17}”, 2.0 – 1.1);とすると以下の出力となりました。

    0.89999999999999991

    デフォルトの書式文字列は”G”であるために勝手に丸められてしまうようですね。

  4. 同趣旨のコメントついてるのに気づきませんでした。申し訳ないです。

コメントを残す