浮動小数点型を使用することの利点にスポットを当てているようです。私はすべての場合で小数を考慮して設計する傾向があり、小数での演算がボトルネックやスローダウンの原因となっているかどうかをプロファイラーに知らせてくれます。これらの場合、私はdoubleまたはfloatに「ダウンキャスト」しますが、それは内部でのみ行い、実行される数学演算の有効桁数を制限することにより、精度の損失を慎重に管理しようとします。
一般に、値が一時的(再利用されない)の場合は、浮動小数点型を使用しても安全です。浮動小数点型の実際の問題は、次の3つのシナリオです。
- 浮動小数点値を集計しています(この場合、精度エラーはさらに複雑になります)。
- 浮動小数点値に基づいて値を作成します(たとえば、再帰的アルゴリズムで)
- 非常に広い有効桁数で計算を行っている(たとえば、
123456789.1 * .000000000000000987654321
)
編集
C#の小数に関するリファレンスドキュメントによると:
小数のキーワードは、128ビットのデータ・タイプを示しています。浮動小数点型と比較して、10進数型は精度が高く範囲が狭いため、財務計算や通貨計算に適しています。
上記のステートメントを明確にするために:
私はすべての場合で小数を考慮して設計する傾向があり、小数での演算がボトルネックやスローダウンの原因となっているかどうかをプロファイラーに知らせてくれます。
私はこれまで、小数が有利な業界で働いたことがあります。物理学やグラフィックスエンジンで作業している場合は、浮動小数点型(floatまたはdouble)を設計する方がはるかに有益です。
Decimalは無限に正確ではありません(プリミティブデータ型で非整数の無限の精度を表すことは不可能です)が、doubleよりはるかに正確です。
- 10進数=有効数字28-29桁
- double =有効数字15〜16桁
- float = 7桁の有効数字
編集2
Konrad Rudolphのコメントに応じて、項目1(上記)は間違いなく正しいです。不正確さの集約は確かに複雑です。例については、以下のコードを参照してください。
private const float THREE_FIFTHS = 3f / 5f;
private const int ONE_MILLION = 1000000;
public static void Main(string[] args)
{
Console.WriteLine("Three Fifths: {0}", THREE_FIFTHS.ToString("F10"));
float asSingle = 0f;
double asDouble = 0d;
decimal asDecimal = 0M;
for (int i = 0; i < ONE_MILLION; i++)
{
asSingle += THREE_FIFTHS;
asDouble += THREE_FIFTHS;
asDecimal += (decimal) THREE_FIFTHS;
}
Console.WriteLine("Six Hundred Thousand: {0:F10}", THREE_FIFTHS * ONE_MILLION);
Console.WriteLine("Single: {0}", asSingle.ToString("F10"));
Console.WriteLine("Double: {0}", asDouble.ToString("F10"));
Console.WriteLine("Decimal: {0}", asDecimal.ToString("F10"));
Console.ReadLine();
}
これは以下を出力します:
Three Fifths: 0.6000000000
Six Hundred Thousand: 600000.0000000000
Single: 599093.4000000000
Double: 599999.9999886850
Decimal: 600000.0000000000
ご覧のとおり、同じソース定数から追加している場合でも、doubleの結果は精度が低く(おそらく正しく丸められます)、floatの精度ははるかに低くなり、浮動小数点は2桁の有効数字。