什么时候应该使用double而不是decimal?

Jam*_*Ide 257 c# floating-point double types decimal

我可以说使用double(或float)代替以下三个优点decimal:

  1. 使用较少的内存.
  2. 更快,因为处理器本身支持浮点数学运算.
  3. 可以代表更大范围的数字.

但这些优势似乎只适用于计算密集型操作,例如建模软件中的操作.当然,在需要精确度时,不应使用双精度数,例如财务计算.那么有没有任何实际的理由选择double(或float)而不是decimal"正常"的应用程序?

编辑补充:感谢所有伟大的回应,我向他们学习.

还有一个问题:一些人认为双打可以更精确地代表实数.宣布时,我认为他们通常也会更准确地代表他们.但是,当执行浮点运算时,准确度是否会降低(有时是显着的)是真实的说法吗?

Nol*_*rin 300

我认为你已经很好地总结了这些优势.但是你缺少一点.该decimal类型在表示基数10时(例如用于货币/金融计算的数字)更准确.一般来说,这种 double类型至少会提供很高的精度(如果我错了,有人会纠正我),并且对任意实数来说肯定会提高速度.简单的结论是:在考虑使用哪种时,double除非您需要提供的base 10准确性,否则始终使用decimal.

编辑:

关于操作后浮点数准确性降低的其他问题,这是一个稍微微妙的问题.实际上,在执行每个操作之后,精度(我在这里可互换地使用术语)将稳定地减少.这是由于两个原因:

  1. 事实上某些数字(最明显的小数)不能以浮点形式真正表示
  2. 发生舍入错误,就像您手动执行计算一样.它在很大程度上取决于上下文(您正在执行多少操作)这些错误是否足够重要,无论如何都需要进行大量考虑.

在所有情况下,如果你想比较理论上应该相等的两个浮点数(但是使用不同的计算得出),你需要允许一定程度的容差(多少变化,但通常非常小) .

有关可以引入精度误差的特定情况的更详细概述,请参阅Wikipedia文章的"准确性"部分.最后,如果您想要在机器级别对浮点数/操作进行认真深入(和数学)的讨论,请尝试阅读经常引用的文章"每个计算机科学家应该知道的关于浮点算术的内容".

  • @Mark:非常简单的例子:0.1是基数2中的周期分数,因此无法在"double"中精确表达.现代计算机仍将打印正确的值,但仅仅是因为它们"猜测"了结果 - 而不是因为它确实正确表达. (24认同)
  • “Decimal”类型的尾数精度为 93 位,而“double”类型的尾数精度约为 52 位。不过,我希望 Microsoft 支持 IEEE 80 位格式,即使它必须填充到 16 个字节;它允许比“double”或“Decimal”更大的范围,比“Decimal”更快的速度,支持超越运算(例如sin(x),log(x)等),以及精度,虽然不完全是与“Decimal”一样好会比“double”好得多。 (2认同)

Mic*_*ows 57

您似乎可以看到使用浮点类型的好处.我倾向于在所有情况下设计小数,并依靠分析器让我知道十进制操作是否导致瓶颈或减速.在这些情况下,我将"向下转换"为double或float,但仅在内部执行,并通过限制正在执行的数学运算中的有效位数来小心地尝试管理精度损失.

通常,如果您的值是瞬态的(不重用),则可以安全地使用浮点类型.浮点类型的真正问题是以下三种情况.

  1. 您正在聚合浮点值(在这种情况下,精度错误复合)
  2. 您可以根据浮点值构建值(例如,在递归算法中)
  3. 您正在使用大量有效数字进行数学运算(例如123456789.1 * .000000000000000987654321)

编辑

根据C#小数参考文档:

所述的十进制关键字表示128位的数据类型.与浮点类型相比,十进制类型具有更高的精度和更小的范围,这使其适用于财务和货币计算.

所以澄清我的上述说法:

我倾向于在所有情况下设计小数,并依靠分析器让我知道十进制操作是否导致瓶颈或减速.

我只在小数有利的行业工作过.如果你正在研究phsyics或图形引擎,那么设计浮点类型(float或double)可能更有利.

十进制不是无限精确的(在原始数据类型中不可能表示非整数的无限精度),但它比双精度要精确得多:

  • decimal = 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();
}
Run Code Online (Sandbox Code Playgroud)

这输出如下:

Three Fifths: 0.6000000000
Six Hundred Thousand: 600000.0000000000
Single: 599093.4000000000
Double: 599999.9999886850
Decimal: 600000.0000000000
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,即使我们从相同的源常量中添加,double的结果也不那么精确(尽管可能会正确地舍入),并且float的精确度要低得多,直到它只减少到只有两位有效数字.

  • @Konrad Rudolph,请参阅“EDIT 2”中的示例作为我试图在第 1 项中提出的观点的证据。通常,这个问题不会表现出来,因为正的不精确性与负的不精确性相平衡,并且它们被洗掉了聚合,但聚合相同的数字(就像我在示例中所做的那样)突出了问题。 (2认同)

Joe*_*Joe 24

使用十进制作为基数10的值,例如财务计算,正如其他人所建议的那样.

但对于任意计算值,double通常更准确.

例如,如果要计算投资组合中每一行的权重,请使用double,因为结果将更接近100%.

在以下示例中,doubleResult比decimalResult更接近1:

// Add one third + one third + one third with decimal
decimal decimalValue = 1M / 3M;
decimal decimalResult = decimalValue + decimalValue + decimalValue;
// Add one third + one third + one third with double
double doubleValue = 1D / 3D;
double doubleResult = doubleValue + doubleValue + doubleValue;
Run Code Online (Sandbox Code Playgroud)

再次以组合为例:

  • 投资组合中每条线的市场价值是货币价值,可能最好用十进制表示.

  • 投资组合中每条线的权重(=市场价值/ SUM(市场价值))通常更好地表示为双倍.


Fly*_*wat 6

当你不需要精确度时,使用double或float,例如,在我写的平台游戏中,我使用浮动来存储玩家的速度.显然我在这里不需要超精确,因为我最终会绕过Int来绘制屏幕.

  • 精度是小数的唯一优势,这是正确的.您不应该问何时应该使用小数点上的浮点数.这应该是你的第一个想法.那么问题是你何时应该使用小数(答案就在这里......当精确度很重要时). (3认同)
  • @Daniel Straight,这很有趣,但我有相反的意见.我认为使用不太精确的类型,因为它的性能特征相当于预优化.在您意识到它的好处之前,您可能需要多次支付预优化费用. (3认同)
  • @Michael Meadows,我能理解这个说法.值得注意的是,过早优化的主要原因之一是程序员不倾向于知道什么会变慢.我们毫无疑问地知道,小数比双打慢.不过,我认为在大多数情况下,无论如何,用户都不会注意到性能提升.当然,在大多数情况下,也不需要精度.嘿. (3认同)

G D*_*ers 5

在某些会计中,考虑使用整数类型替代或结合使用的可能性。例如,假设您所遵循的规则要求每个计算结果至少保留 6 位小数,并且最终结果将四舍五入到最接近的便士。

100 美元的 1/6 的计算结果为 16.66666666666666...,因此工作表中的值将为 16.666667 美元。double 和decimal 都应将结果精确到小数点后6 位。但是,我们可以通过将结果作为整数 16666667 向前推进来避免任何累积误差。后续的每次计算都可以使用相同的精度进行,并类似地向前推进。继续这个例子,我计算了该金额的德克萨斯州销售税 (16666667 * .0825 = 1375000)。将两者相加(这是一个简短的工作表)1666667 + 1375000 = 18041667。将小数点向后移,得到 18.041667,即 18.04 美元。

虽然这个简短的示例不会使用双精度或小数产生累积误差,但很容易说明简单计算双精度或小数并结转会累积显着误差的情况。如果您所操作的规则要求小数位数有限,则将每个值存储为整数,方法是乘以 10^(所需的小数位数),然后除以 10^(所需的小数位数)以获得实际值值将避免任何累积误差。

在不存在小数便士的情况下(例如自动售货机),根本没有理由使用非整数类型。只需将其视为数便士,而不是美元。我见过代码中每次计算只涉及整便士,但使用 double 会导致错误!仅整数数学解决了这个问题。所以我非常规的答案是,如果可能的话,放弃双精度和十进制。