十进制vs双! - 我应该使用哪一个?何时使用?

Son*_*Ali 851 c# precision double currency decimal

我一直看到人们在C#中使用双打.我知道我读到某个地方,双打有时会失去精确度.我的问题是什么时候应该使用双倍,何时应该使用小数类型?哪种类型适合货币计算?(即超过1亿美元)

Dav*_*vid 1002

为了钱,总是小数.这就是为什么它被创造出来的原因.

如果数字必须正确加或平衡,请使用小数.这包括人们可能手工完成的任何财务存储或计算,分数或其他数字.

如果数字的确切值不重要,请使用double表示速度.这包括图形,物理或其他物理科学计算,其中已经存在"有效位数".

  • 这就是为什么你使用Decimal的原因:Double的精确度只有16位十进制数字,经过几次算术运算后,错误会迅速累积到足以爬进15,14,13等数字.舍入到"美分"要求在美分数字后至少有一位完全准确的数字,但实际上你应该保留4或5来隔离累积的算术错误,你不能允许它破​​坏你用来围绕美分的百分之一列.剩下16(总) - 2(美分) - (4或5错误填充)=哦$ $只有7(或更少)可靠的整数位数为您的钱! (72认同)
  • 只是为了揉搓...如果你正在建造一个游戏,你真的会关心你刚刚撞到一块四分之一英里的炸药桶落在距离目标1/16英寸的地方,因为累积误差超过了数百个"位置+(速度*时间)"步骤?我对此表示怀疑. (49认同)
  • 并不是双精度是不准确的 - 它具有*相对*精度,并且可以表示十进制根本无法处理的非常大或小的幅度. (38认同)
  • 要清除它,双倍没有16位数 - 这只是*有意义的*位数.浮点数基于基数2数学中的指数 - 一些基数为10的数字被破坏,因为它们是无限系列,如果转换为基数2 exp,则二进制浮点数数字`0.1*0.1!= 0.01`因为0.1无法精确表示.数学运算也会导致漂移 - 加上和减去美元和美分,你可以得到像0.9999999999999这样的数字.toString()最初通过舍入来隐藏它,但是精确的比较会立即被破坏. (19认同)
  • 因此,我不会操纵超过9.99美元(1整数位)的货币值,因为而不是4或5位错误累积填充,我想要更多像10或11.因为十进制是一个128位数字,它给你那种隔离,即使是数百万亿美元的数字,因为它具有28-29位数的准确性.但是,你不能高于此.999,999,999,999,999.99R(999万亿)需要18位精度来正确舍入,并且由于十进制给你28-29,这只是累积算术错误绝缘的10位数. (17认同)
  • @Triynko:请注意,十进制是基数10,这意味着它可以表示货币值,如$ 0.30*完全*.这意味着只要您只进行加法,减法和乘以整数,就不会有任何舍入误差.因此它没有28位精度,它具有完美的准确性.与二进制浮点相比,这是一个巨大的差异,如double,它不能完全代表0.30美元. (11认同)
  • @cbp - 你链接的问题是VB库,而VB没有小数.这与这次讨论完全无关. (2认同)
  • @Arijoon我相信这实际上是十进制类型为您执行的操作,只是它可以适当地除以大于100的数字,因为某些系统会保留2位数以上的精度。(例如,比特币。)关键是十进制类型除以10的幂,而双精度除以2的幂。 (2认同)

Mic*_*rdt 173

我的问题是什么时候应该使用双倍,何时应该使用小数类型?

decimal 因为当你使用10 ^(+/- 28)范围内的值时,你对基于10个表示的行为有所期望 - 基本上是金钱.

double因为当你需要相对准确度(即在大值上的拖尾数字中丢失精度不是一个问题)时,它们的大小差别很大 - double覆盖超过10 ^(+/- 300).科学计算是这里最好的例子.

哪种类型适合货币计算?

十进制,十进制,十进制

不接受任何替代品.

最重要的因素是,double作为二进制分数实现,根本不能准确地表示许多decimal分数(如0.1),并且其总位数较小,因为它是64位宽而128位decimal.最后,财务应用程序通常必须遵循特定的舍入模式(有时是法律规定的).decimal 支持这些 ; double才不是.

  • @MichaelBorgwardt 当您说“十进制、*十进制*、**十进制**”时,我应该使用哪一个? (3认同)
  • 毫无疑问,在表示财务价值时不会使用"double",但是当你写"double"不支持特定的舍入模式时,你的确是什么意思,与"十进制"相比?AFAIK,`Math.Round`有重载,接受`double`和`decimal`的`MidpointRounding`参数? (2认同)
  • @Groo:我想我必须查看.Net 1.1 API,该方法是在2.0中添加的 - 但由于二进制分数存在问题,它仍然有点无意义.当前API文档中有一个示例说明了此问题. (2认同)

Chr*_*s S 38

System.Single/float - 7位数字
System.Double/double - 15-16位数字
System.Decimal/decimal - 28-29有效数字

我使用错误的类型(好几年前)被蜇的方式是大量的:

  • £520,532.52 - 8位数
  • £1,323,523.12 - 9位数

你花了一百万用于浮动.

15位数的货币价值:

  • £1,234,567,890,123.45

9万亿双倍.但是通过划分和比较它会更复杂(我绝对不是浮点数和无理数的专家 - 请参阅Marc的观点).混合小数和双精度会导致问题:

如果使用十进制数,使用浮点数的数学或比较操作可能不会产生相同的结果,因为浮点数可能不完全接近十进制数.

什么时候应该使用double而不是decimal?有一些类似和更深入的答案.

使用double而不是decimal 货币应用程序是一种微观优化 - 这是我看待它的最简单方法.


Ian*_*oyd 35

十进制是确切的值.Double表示近似值.

USD: $12,345.67 USD (Decimal)
CAD: $13,617.27 (Decimal)
Exchange Rate: 1.102932 (Double)
Run Code Online (Sandbox Code Playgroud)

  • 十进制不是精确值.根据文档,Decimal提供28-29个十进制数字的精度.十进制不执行分析算术,因此不是"精确".十进制对于金钱很有用,因为即使价值数万亿美元,它仍然会给你带来10位数的绝对累积算术误差,同时仍然可以精确地舍入到美分. (11认同)
  • 为什么汇率加倍而不是小数?这不仅仅是加元1美元的价格吗? (5认同)
  • @gerrit汇率不是加元1美元的*"价格"*.它是两者价值的*比率*.根据您的来源确定您将获得多少小数位.例如,1美元的价值为1.0016加元.1 Great Britian Pound价值1.5909加元.1越南盾的价值0.000048加元.它是一个*比率*因此在任何地方都不会被实际截断而不会失去精度. (3认同)

Cle*_*man 27

为了钱:decimal.它花费了更多的记忆,但没有像double有时那样的四舍五入的麻烦.

  • 它具有四舍五入的所有麻烦:尝试`1m/3m + 1m/3m == 2m/3m`.主要的区别是 - 有效数字的位数更多,最重要的是:在除数的素数因子化中对5的数字进行操作时没有精度损失.例如.`1m/5m + 1m/5m`将完全等于`2m/5m`. (9认同)

Ott*_*ger 10

绝对使用整数类型进行货币计算.这一点不够强调,因为乍一看似乎浮点类型就足够了.

这是python代码中的一个例子:

>>> amount = float(100.00) # one hundred dollars
>>> print amount
100.0
>>> new_amount = amount + 1
>>> print new_amount
101.0
>>> print new_amount - amount
>>> 1.0
Run Code Online (Sandbox Code Playgroud)

看起来很正常.

现在用10 ^ 20津巴布韦元再试一次

>>> amount = float(1e20)
>>> print amount
1e+20
>>> new_amount = amount + 1
>>> print new_amount
1e+20
>>> print new_amount-amount
0.0
Run Code Online (Sandbox Code Playgroud)

如你所见,美元消失了.

如果你使用整数类型,它工作正常:

>>> amount = int(1e20)
>>> print amount
100000000000000000000
>>> new_amount = amount + 1
>>> print new_amount
100000000000000000001
>>> print new_amount - amount
1
Run Code Online (Sandbox Code Playgroud)

  • 您甚至不需要非常大/小的值来找到双精度base2近似值与实际基数10值之间的差异,许多小值无法准确存储.计算"1 - 0.1 - 0.9"(确保编译器不优化方程式),并将其与零进行比较.你会发现,对于双打,结果就像是2e-17而不是0(确保你运行比较,因为许多print/ToString函数会在一定数量的小数位后加倍,以消除这些类型的错误). (7认同)
  • 整数 ?!当你有 1.5 美元时会发生什么? (3认同)
  • @Noctis如果你考虑一下,你会想出一个解决方案 (2认同)
  • 出于准确性目的(可能是出于性能原因),没有理由使用 `int` 而不是 `decimal`。避免使用“double”,而使用“decimal”。Decimal 使用基数为 10 的指数,因此在解析基数为 10 的值(如 0.1)时,您不会遇到与使用 double 相同的二进制舍入错误。 (2认同)

Hon*_*scz 5

我认为位宽旁边的主要区别是十进制有指数基数10而双数有2

http://software-product-development.blogspot.com/2008/07/net-double-vs-decimal.html