浮動小数点型を使用する利点に注目しているようです。私は常に 10 進数を考慮して設計する傾向があり、10 進数の演算がボトルネックやスローダウンを引き起こしているかどうかをプロファイラーに知らせてくれます。そのような場合、double または float に「ダウン キャスト」しますが、これは内部的にのみ行い、実行される数学演算の有効桁数を制限することで精度の低下を慎重に管理しようとします。
一般に、値が一時的な (再利用されない) 場合は、浮動小数点型を安全に使用できます。浮動小数点型に関する実際の問題は、次の 3 つのシナリオです。
- 浮動小数点値を集計しています (この場合、精度エラーが悪化します)
- 浮動小数点値に基づいて値を作成します (再帰アルゴリズムなど)。
- 有効桁数が非常に多い計算をしている (例:
123456789.1 * .000000000000000987654321
)
編集
C# 10 進数に関するリファレンス ドキュメントによると、次のようになります。
decimalキーワードは、128 ビットのデータ型を示します。浮動小数点型と比較して、10 進数型は精度が高く、範囲が狭いため、金融および通貨の計算に適しています。
したがって、私の上記の声明を明確にするために:
私は常に 10 進数を考慮して設計する傾向があり、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桁。