どういうこと?
下記のソースコードを実行するとなぜか結果は100
になりません。
double sumData = 0;
for (int i = 1; i <= 1000; i++)
{
//0.1を1000回足す!
sumData += 0.1;
}
Console.WriteLine(sumData);
//出力:99.9999999999986
なんで?
参考にしたサイトがとても分かりやすく解説されているのでそのまま引用します。
SingleやDoubleのような浮動小数点数型は、値を2進数で格納しています。しかし、ほとんどの10進数の小数は2進数で表現することができません。そのためこのような値はSingleやDouble型では近似値でしか表現することができず、その誤差が上記のような非常識的な計算結果として現れます。
...(´ε` )ふぅん(今まで実装してきたソフト大丈夫かな...)
どうすんの?
Decimal型
を使います。
上記で引用したサイトがこれまた分かりやすくされているのでそのまま引用します。
10進数の小数を2進数に変換するときの丸め誤差をなくすには、SingleやDouble型の代わりにDecimal型を使用します。Decimal型は10進数の小数でも正確に表現することができます。
ということでソースコードのsumData
変数をDecimal
型にして再度試してみました。
decimal sumData = 0;
for (int i = 1; i <= 1000; i++)
{
//0.1を1000回足す!
sumData += (decimal)0.1;
}
Console.WriteLine(sumData);
//出力:100.0
どっち使えばいいの?
- 基本的には
double
型を使います。 - お金などが関わっている場合(勘定ソフトや会計処理)は
decimal
型を使います。
理由
decimal
型の処理は他の型に比べて計算がくっそ遅いです。ただし、速度よりも精度を求められる場面ではdecimal
型を採用しないと正しくない計算結果が算出される可能性があります。ちなみにdouble
型の有効桁数は15~17桁です。
それぞれの型で0.1を1億回足すのにかかる時間を検証しました。
結果は下記になりました。
single | double | decimal |
---|---|---|
2138[ms] | 2105[ms] | 14784[ms] |
decimal sumData = 0;
var sum = (decimal)0.1;
var sw = Stopwatch.StartNew();
for (int i = 1; i <= 1000000000; i++)
{
//0.1を1000000000回足す!
sumData += sum;
}
sw.Stop();
Console.WriteLine($"{sw.ElapsedMilliseconds}[ms]");
余談
プログラミング学習1ヶ月目の時に電卓を作りましたが、あの時のソースコードを見てみたらdouble
型を使ってました。(゚A゚;)ゴクリ...(世の中に出るソフトじゃなくて良かったぁ)
ここまで読んで頂いてありがとうございました!