您似乎很了解使用浮点类型的好处。我倾向于在所有情况下都设计小数,并依靠分析器让我知道小数操作是否会导致瓶颈或减速。在这些情况下,我将“向下转换”为 double 或 float,但仅在内部进行,并通过限制正在执行的数学运算中的有效数字的数量来仔细尝试管理精度损失。
通常,如果您的值是瞬态的(未重用),则可以安全地使用浮点类型。浮点类型的真正问题是以下三种情况。
- 您正在聚合浮点值(在这种情况下,精度误差复合)
- 您基于浮点值构建值(例如在递归算法中)
- 您正在使用大量有效数字进行数学运算(例如,
123456789.1 * .000000000000000987654321
)
编辑
根据C# decimals 的参考文档:
decimal关键字表示 128 位数据类型。与浮点类型相比,十进制类型的精度更高,范围更小,适用于金融和货币计算。
因此,为了澄清我的上述陈述:
我倾向于在所有情况下都设计小数,并依靠分析器让我知道小数操作是否会导致瓶颈或减速。
我只在小数有利的行业工作过。如果您正在研究物理或图形引擎,那么设计浮点类型(float 或 double)可能更有益。
Decimal 不是无限精确的(不可能在原始数据类型中表示非整数的无限精度),但它比 double 精确得多:
- 十进制 = 28-29 位有效数字
- 双 = 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 的精确度要低得多,以至于它已经减少到只有两位有效数字。