2009-04-29 125 views
228

我可以命名三个好处使用double(或float)代替decimal什么时候应该使用double而不是小数?

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

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

编辑补充: 感谢所有的好评,我从他们那里学到了。

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

+0

另请参阅http://stackoverflow.com/questions/2545567/in-net-how-do-i-choose-between-a-decimal-and-a-double –

+3

这得到upvoted相当有规律,我仍然奋斗用它。例如,我正在开发一个用于财务计算的应用程序,因此我在整个过程中使用了十进制。但数学和VisualBasic.Financial函数使用双重,所以有很多转换,我不断猜测使用小数。 –

+0

@JamieIde这是疯狂的金融功能使用双倍,金钱应该始终在十进制。 –

回答

273

我认为你已经总​​结了很好的优势。然而你错过了一点。 decimal类型仅在表示基数10数字(例如在货币/金融计算中使用的数字)时更精确。一般来说,double类型将提供至少同样的精度(如果我错了,有人会纠正我),对于任意实数,速度肯定会更快。简单的结论是:在考虑使用哪一个时,请始终使用double,除非您需要decimal提供的base 10精度。

编辑:

关于你提到的有关操作之后浮点数的精度降低额外问题,这是一个稍微更微妙的问题。事实上,每次操作完成后,精度(我在这里使用术语“可互换”来表示准确度)会稳步下降。这是由于两个原因:a)某些数字(最明显的小数)不能以浮点形式真实地表示,b)发生舍入误差,就像您手动进行计算一样。它很大程度上取决于上下文(您正在执行多少操作),但是这些错误是否足够重要,以致需要考虑很多问题。在所有情况下,如果要比较理论上应该等价的两个浮点数(但是使用不同的计算),则需要允许一定程度的容差(多少变化,但通常非常小) 。

有关可以引入精度误差的特定情况的更详细概述,请参阅Wikipedia article的精度部分。最后,如果你想在机器级对浮点数/操作进行认真深入的(和数学的)讨论,请阅读经常引用的文章What Every Computer Scientist Should Know About Floating-Point Arithmetic

+1

你能否提供一个基数为10的数字的例子,在转换为基数2时会失去精度? –

+0

@Mark:1.000001就是一个例子,至少根据Jon Skeet的说法。 (请参阅本页的问题3:http://www.yoda.arachsys.com/csharp/teasers-answers.html) – Noldorin

+18

@Mark:非常简单的例子:0.1是基数为2的周期性分数,因此无法准确表达在'双'。现代计算机仍然会打印正确的值,但仅仅是因为他们“猜测”了结果 - 并不是因为它确实表达正确。 –

6

当你不需要精度的时候使用双精度或浮点数,例如在我写的一个平台游戏中,我用浮点数来存储玩家速度。很明显,我不需要超精确度,因为我最终轮到Int来在屏幕上绘图。

+3

精度是小数的唯一优点,这是正确的。你不应该问什么时候应该使用浮点数超过小数。这应该是你的第一个想法。那么问题是什么时候你应该使用小数(当答案在这里时......当精度很重要时)。 –

+2

@Daniel直,这很有趣,但我有相反的意见。我认为使用不太精确的类型是因为它的性能特征等于预优化。在您意识到其优点之前,您可能需要多次支付预优化费用。 –

+3

@Michael Meadows,我可以理解这个说法。需要注意的是,过早优化的主要抱怨之一是程序员不会知道什么会变慢。不过毫无疑问,我们知道小数比双打慢。尽管如此,我想大多数情况下,用户无论如何都不会注意到性能的提升。当然,在大多数情况下,精度也不是必需的。嘿。 –

50

您似乎很清楚使用浮点类型的好处。我倾向于在所有情况下设计小数点,并依靠分析器让我知道小数点操作是否会导致瓶颈或减速。在这些情况下,我会“下注”来双倍或浮动,但只能在内部完成,并且仔细尝试通过限制正在执行的数学运算中的有效数字的数量来管理精度损失。

一般来说,如果你的值是瞬态的(不重用),你可以安全地使用浮点类型。浮点类型的真正问题是以下三种情况。

  1. 您正在聚集浮点值(在这种情况下,精度误差化合物)
  2. 你建
  3. 你正在做数学与基于所述浮点值(例如在一个递归算法)值非常宽的数目的数字显著(例如,123456789.1 * .000000000000000987654321

EDIT

甲ccording到reference documentation on C# decimals

小数关键字表示 128位的数据类型。与 浮点类型相比,小数类型 具有更高的精度和更小的范围,因此适用于 财务和货币计算。

所以澄清我的上述声明:

我倾向于在所有 情况下为小数设计,并依靠探查让 我知道,如果在十进制运算是 造成瓶颈或慢-downs。

我只在有小数的行业工作过。如果您正在使用phsyics或图形引擎,那么设计浮点类型(float或double)可能会更有益处。

小数是不是无限精确的(这是不可能代表无限精度用于在原始数据类型非整数),但它远远大于双更精确的:

  • 小数= 28-29显著数字
  • 双= 15-16显著数字
  • 浮子= 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 

正如你所看到的,即使我们从同一来源不断增加,双重的结果是少精确的(尽管可能会正确地回合),并且浮点数远不够精确,直到它减少到只有两个有效数字。

+1

点1不正确。精度/舍入误差只发生在铸造中,而不是计算中。它*当然是正确的,因为大多数数学运算是不稳定的,因此乘以误差。但这是另一个问题,它适用于所有有限精度的数据类型,所以特别适用于小数。 –

+1

@Konrad Rudolph,请参阅“编辑2”中的示例,作为我在第1项中试图提出的观点的证据。通常,这个问题并不表现出来,因为正面的不精确性与负面的不精确性相平衡,但总计相同的数字(正如我在示例中所做的那样)强调了这个问题。 –

+0

很好的例子。刚刚向我的初级开发人员展示过,孩子们都很惊讶。 – Machado

3

如果您需要与其他语言或平台进行二进制运算,那么您可能需要使用float或double,这些都是标准化的。

0

如果您重视性能而不是正确性,请使用浮点数。

+6

十进制数不是更正确的,除非在某些有时(有时并非总是)重要的有限情况下。 –

0

选择您的应用程序的功能类型。如果你需要像财务分析那样精确,你已经回答了你的问题。但是如果你的应用程序可以用一个估计来解决你的问题,你就可以加倍。

您的应用程序是否需要快速计算,或者他是否有足够的时间在世界范围内为您提供答案?这真的取决于应用程序的类型。

饥肠辘辘的图形?浮动或双重就够了。财务数据分析,流星打击一个星球的精度?那些将需要一点精确度:)

+8

十进制数字也是估计值。它们符合金融算术的惯例,但是在涉及物理学的计算中没有优势。 –

21

对基数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; 

所以再次采取组合的例子:

  • 每一行的组合中的市场价值是一个货币值,可能最好用十进制表示。

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

+1

+1简单而简单的例子! –

-1

十进制具有较宽的字节,double本身由CPU支持。十进制是基数为10,所以计算小数时会发生小数到小数的转换。

For accounting - decimal 
For finance - double 
For heavy computation - double 

请记住,.NET CLR只支持Math.Pow(double,double)。小数不被支持。如果该符号比小数显示较短

.NET框架4

[SecuritySafeCritical] 
public static extern double Pow(double x, double y); 
0

双值将序列化到科学记数法默认情况下。 (例如.00000003将是3e-8)十进制值永远不会序列化为科学记数法。序列化供外部消费时,这可能是一个考虑因素。

4

在一些会计,考虑使用整型代替或结合的可能性。例如,假设您所运行的规则要求每个计算结果都带有至少6位小数,并且最终结果将四舍五入为最接近的一分钱。

$ 100 1/6日的计算得到$ 16.66666666666666 ......,所以在工作表中携带着价值为$ 16.666667。双精度和小数精度都应该精确到小数点后6位。但是,我们可以通过将结果作为整数16666667来避免任何累积误差。每个后续计算都可以以相同的精度进行并进行类似的处理。继续举例,我计算德克萨斯州的销售税(16666667 * .0825 = 1375000)。添加这两个(这是一个简短的工作表)1666667 + 1375000 = 18041667.将小数点移回给予我们18.041667或$ 18.04。

虽然这个简单的例子就不会产生使用双精度或十进制累积误差,这也很容易表现出在那里简单地计算双精度或十进制和发扬会累积显著错误情况。如果您在下面操作的规则需要有限的小数位数,则将每个值存储为整数乘以10 ^(所需的小数点位数),然后除以10 ^(所需的小数位数),以获得实际值值将避免任何累积错误。

在不发生便士馏分情况下(例如,自动贩卖机),没有理由使用非整数类型的。简单地把它看作数钱,而不是美元。我看过代码,其中每个计算只涉及整个便士,但使用double会导致错误!整数只有数学解决了这个问题。所以我非常规的答案是,如果可能,放弃双精度和小数。

2

注意:这篇文章是基于http://csharpindepth.com/Articles/General/Decimal.aspx和小数类型功能的信息我自己的是什么意思解释。我将假设Double是IEEE的双精度。

注2:最小和最大在这个岗位reffer至数的大小。

“十进制”的优点。

  • “十进制”可以精确地表示可写为(十分短)十进制小数的数字,double不能。在金融分类账和类似的地方,这一点非常重要,重要的是结果必须与计算人员进行的计算完全匹配。
  • “十进制”的尾数比“double”大得多。这意味着对于标准化范围内的值“decimal”将具有比double更高的精度。小数

    缺点它就会慢很多(我没有基准,但我想也许更多猜测至少一个数量级),小数将不会从任何硬件加速受益,对它进行的算术将需要相对昂贵的乘法/除以10的乘方(这比乘以2的乘方的乘法和除法要昂贵得多),以在加/减之前匹配指数并且在乘法/除法之后使指数回到范围内。

  • 小数点将溢出更早的双倍会。十进制只能表示数字,最多为±2 -1。通过比较double可以代表数字达到近似值±2
  • 小数点会提前下溢。以小数表示的最小数字是±10 -28。通过对比双可以表示的值下降到2 -149(约10 -45 )如果subnromal支援数字和2 -126(约10 -38 ),如果他们不。
  • 十进制占用两倍的内存。

我的意见是,你应该默认使用“十进制”金钱工作和其他情况下,其中匹配人的计算是重要的,你应该使用双重作为您的默认选择余下的时间。

0

取决于你需要什么。

因为float和double是二进制数据类型,你有一些 diifculties和errrors在发号的方式,因此例如双将0.1四舍五入为0.100000001490116,双也会轮1/3〜0.33333334326441。简单地说,并非所有实数都有精确的双精度表示形式

幸运的是C#还支持所谓的十进制浮点运算,其中数字通过十进制数字系统而不是二进制系统表示。因此,在存储和处理浮点数时,十进制浮点数算术不会丢失精度。这使得它非常适合需要高精度的计算。

相关问题