您似乎发现使用浮点类型的好处。我倾向于在所有情况下都针对小数进行设计,并依靠探查器让我知道对小数的操作是否会导致瓶颈或速度下降。在这些情况下,我将“下转换”为两倍或浮点数,但只能在内部进行,并尝试通过限制所执行的数学运算中的有效位数来管理精度损失。
通常,如果您的值是瞬时值(不可重用),则可以安全地使用浮点类型。浮点类型的真正问题是以下三种情况。
- 您正在汇总浮点值(在这种情况下,精度误差会增加)
- 您基于浮点值构建值(例如,在递归算法中)
- 您正在使用很多有效数字进行数学运算(例如,
123456789.1 * .000000000000000987654321
)
编辑
根据有关C#小数的参考文档:
所述的十进制关键字表示128位的数据类型。与浮点类型相比,十进制类型具有更高的精度和更小的范围,这使其适用于财务和货币计算。
因此,请澄清我的上述陈述:
我倾向于在所有情况下都针对小数进行设计,并依靠探查器让我知道对小数的操作是否会导致瓶颈或速度下降。
我只在小数点有利的行业工作过。如果您使用的是物理或图形引擎,则设计浮点类型(float或double)可能会更有益。
十进制不是无限精确的(不可能为原始数据类型中的非整数表示无限精确度),但是它比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的精确度也很差,以至于其减小到仅两个有效数字。