什么时候应该使用双精度而不是小数?
我可以列举使用double
(或float
)而不是decimal
三个优点:
- 使用较less的内存。
- 因为浮点math运算本来就是由处理器支持的,所以速度更快。
- 可以代表更大范围的数字。
但是,这些优势似乎只适用于计算密集型操作,如build模软件中的操作。 当然,如果需要精确度,例如财务计算,则不应使用双打。 那么在“正常”应用程序中是否有任何实际的理由selectdouble
float
(或float
)而不是decimal
?
编辑补充说:感谢所有的好评,我从他们那里学到了。
还有一个问题:有些人认为双打可以更精确地代表实数。 当宣布的时候,我会认为他们通常会更准确地代表他们。 但是,当浮点运算被执行时,精确度是否会降低(有时甚至是显着的)呢?
我想你已经把这个优势总结得很好。 但是你错过了一点。 decimal
types只能代表基数为10的数字(例如在货币/财务计算中使用的数字)。 一般来说, double
精度型的精度至less是一样的(如果我错了,那么有人会纠正我),对于任意实数,速度肯定会更快。 简单的结论是:在考虑使用哪一个的时候,除非你需要decimal
提供的base 10
精度,否则总是使用double
。
编辑:
关于操作之后浮点数精度下降的问题,这是一个稍微微妙的问题。 事实上,每次操作完成后,精确度(我在这里使用的术语可以互换)的精度会稳步下降。 这是由于两个原因:a)某些数字(最明显的小数)不能以浮点forms真实地表示,b)发生舍入错误,就像您手动进行计算一样。 这很大程度上取决于上下文(您正在执行多less操作),但是这些错误是否足够重要,以致需要考虑更多。 在所有情况下,如果要比较两个理论上应该相等的浮点数(但是使用不同的计算),则需要允许一定程度的容差(多less变化,但通常非常小) 。
有关可以引入准确度错误的特定情况的更详细概述,请参阅维基百科文章的准确性部分。 最后,如果您希望在机器级别上对浮点数/操作进行深入的(和math的)讨论,请阅读常用计算机科学家关于浮点运算的常识 。
你似乎有点使用浮点types的好处。 我倾向于在所有情况下devise小数点,并依靠一个分析器让我知道小数点操作是否会造成瓶颈或减速。 在这种情况下,我会“下注”加倍或浮动,但是只能在内部进行,并且通过限制正在执行的math运算中的有效位数来谨慎地pipe理精度损失。
一般来说,如果你的值是瞬态的(不重用),你可以安全地使用浮点types。 浮点types的真正问题是以下三种情况。
- 您正在汇总浮点值(在这种情况下精度错误复合)
- 您可以基于浮点值构build值(例如,在recursionalgorithm中)
- 你正在做一个有很多有效数字的math(例如
123456789.1 * .000000000000000987654321
)
编辑
根据C#小数的参考文档 :
十进制关键字表示一个128位的数据types。 与浮点types相比,小数types具有更高的精度和更小的范围,适用于财务和货币计算。
所以要澄清我的上述说法:
我倾向于在所有情况下devise小数点,并依靠分析器让我知道小数点操作是否会造成瓶颈或减速。
我只在有小数的行业工作过。 如果您正在使用phsyics或graphics引擎,则devise浮点types(浮点型或双精度型)可能会更有帮助。
十进制不是无限精确的(在基本数据types中不可能表示非整数的无限精度),但它比双精度要精确得多:
- 十进制= 28-29有效数字
- 双= 15-16有效数字
- float = 7位有效数字
编辑2
为了回应康拉德·鲁道夫的评论,第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
正如你所看到的,即使我们从相同的源常量添加,双精度的结果是不太精确的(尽pipe可能会正确地舍入),并且浮点精度要低得多,直到精简为止两位有效数字。
像其他人所build议的那样,使用基数为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;
所以再以一个投资组合为例:
-
投资组合中每一条线的市场价值都是货币价值,可能最好用十进制表示。
-
投资组合中每条线的权重(=市场价值/总市值)通常更好地表示为双倍。
当你不需要精确度的时候,使用双精度或浮点数,例如,在我写的一个平台游戏中,我使用了一个浮点数来存储玩家的速度。 很明显,我不需要超精确的,因为我最终在屏幕上绘制一个Int。
在某些会计中,考虑使用整数types替代或联合的可能性。 例如,假设您所运行的规则要求每个计算结果都带有至less6位小数,最终结果将四舍五入到最接近的一个分数。
计算$ 100的1/6,得到$ 16.66666666666666 …,所以工作表中的价值将是$ 16.666667。 双精度和小数精度都应该精确到小数点后6位。 但是,我们可以通过将结果作为整数16666667来避免任何累积误差。每个后续的计算可以以相同的精度进行并且相似地进行。 继续这个例子,我计算德克萨斯州的销售税(16666667 * .0825 = 1375000)。 添加两个(这是一个简短的工作表)1666667 + 1375000 = 18041667.小数点移回给我们18.041667,或$ 18.04。
虽然这个简短的例子不会产生使用双精度或十进制的累积误差,但是显示简单计算双精度或十进制的情况相当容易,并且前进将积累显着的误差。 如果您在下面操作的规则需要有限的小数位数,则将每个值存储为一个整数乘以10 ^(所需的小数位数),然后除以10 ^(所需的小数位数),以获得实际值值将避免任何累积错误。
在几分之一便士没有出现的情况下(例如自动售货机),根本没有理由使用非整数types。 简单地把它看作是数钱而不是美元。 我已经看到了代码,每个计算只涉及整个便士,但使用双导致错误! 整数只有math解决了这个问题。 所以我非常规的答案是,如果可能,放弃双精度和小数。
如果您需要与其他语言或平台进行二进制运算,则可能需要使用float或double,这些是标准化的。
注意:这篇文章是基于http://csharpindepth.com/Articles/General/Decimal.aspx中的十进制typesfunction的信息以及我自己对这个意思的解释。; 我将假设双精度是正常的IEEE双精度。
注2:在这篇文章中最小最大的数字反映了数字的大小。
优点“十进制”。
- “小数”可以精确地表示可以写成(十分短)十进制小数的数字,double不能。 在金融分类账和类似的地方,这一点非常重要,重要的是结果必须与人类的计算结果完全一致。
- “十进制”的尾数要比“双倍”大得多。 这意味着对于其范围内的值“十进制”将具有比双精度高得多的精度。
小数的缺点
- 它会慢得多(我没有基准,但我猜可能会更多),小数不会受益于任何硬件加速和算术,它将需要相对昂贵的乘法/除以10的权力(这比乘以和除以2)的幂更昂贵,以便在加/减之前匹配指数并且在乘/除之后使指数回到范围内。
- 十进制将溢出更早的双倍意志。 小数只能表示最高±2 96 -1的数字。 通过比较double可以表示数字高达近±2 1024
- 小数点会提前下溢。 以小数表示的最小数字是±10 -28 。 通过比较,如果支持次正数,double可以表示2-149(大约10-45),如果不支持则取2-126(大约10-38)。
- 十进制占用两倍的内存。
我的意见是,你应该默认使用“十进制”的金钱工作和其他情况下,匹配人的计算确切是重要的,你应该使用双重作为您的默认select余下的时间。
如果您重视正确性,请使用浮点数。
select你的应用程序的functiontypes。 如果您需要像财务分析那样的精确性,您已经回答了您的问题。 但是,如果你的应用程序可以解决你的估计你可以加倍。
您的应用程序是否需要快速计算?或者他是否有足够的时间在世界上给您一个答案? 这真的取决于应用程序的types。
graphics饿了? 浮动或双重就够了。 财务数据分析,meteor打击一个星球的精度? 那些需要一点精确度:)
默认情况下,double值将序列化为科学记数法,如果该记法小于十进制显示。 (例如.00000003将是3e-8)十进制值永远不会序列化为科学记数法。 序列化供外部消费时,这可能是一个考虑因素。
取决于你需要什么。
因为float和double是二进制数据types,所以在rounds数字中有一些 diifculties和errrors,所以比如double会在0.1到0.100000001490116之间,double也会在1/3到0.33333334326441之间。 简而言之,并不是所有的实数都有双精度型的精确表示
幸运的是,C#也支持所谓的十进制浮点运算,其中数字通过十进制数字系统而不是二进制系统来表示。 因此,在存储和处理浮点数时,十进制浮点运算不会失去准确性 。 这使得它非常适合需要高精度的计算。
十进制有更宽的字节,双倍本地CPU支持。 十进制是基数为10,所以计算小数时会发生小数到小数的转换。
For accounting - decimal For finance - double For heavy computation - double
请记住,.NET CLR只支持Math.Pow(double,double)。 小数不被支持。
.NET Framework 4
[SecuritySafeCritical] public static extern double Pow(double x, double y);