C#中用于货币的最佳数据类型是什么?

510

C#中用于货币的最佳数据类型是什么?


5
你可能会在这个帖子中找到有用的答案。 - ntombela
1
以下是所有数据类型的映射:https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/sql-server-data-type-mappings - JohnLBevan
另外,如果使用数据注释,请包括 using System.ComponentModel.DataAnnotations; ... [DataType(DataType.Currency)] https://msdn.microsoft.com/zh-cn/library/system.componentmodel.dataannotations.datatype(v=vs.110).aspx - JohnLBevan
9个回答

506

decimal所述:

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

您可以按以下方式使用decimal:

decimal myMoney = 300.5m;

49
您应该解释那个链接的重要性。答案本身应足够好,链接可以作为额外参考或细节。请参阅http://stackoverflow.com/help/how-to-answer。 - TheRubberDuck
2
所以最短的答案可以比最短的评论少得多 - 有趣!我并不反对简洁明了的回答,特别是当它还“深入”地链接到进一步的讨论时。 - B. Clay Shannon-B. Crow Raven
7
非常棒的答案,我认为它已经完全回答了问题,不需要进一步解释。提供MSDN文档链接也是个额外的加分项。拍手喝彩! - trnelson

126

System.Decimal

十进制数值类型(Decimal)表示从正79,228,162,514,264,337,593,543,950,335到负79,228,162,514,264,337,593,543,950,335之间的十进制数。Decimal类型适用于需要大量有效整数和小数位且不需要舍入误差的金融计算。Decimal类型不能完全消除舍入的需要,但它可以最小化由于舍入引起的误差。

我想指向zneak关于为什么不能使用double来表示货币的这个优秀回答


82

3
我本来就想建议这个,但我将货币设为一个类,这样我就可以定义一个汇率(相对于“基准货币”,通常是美元[我将其设置为1.00的汇率])。 - Thomas Owens
6
对于像我这样未来的访问者,现在有这个:https://www.nuget.org/packages/Money/ 它很强大! - Korijn
想知道这种类型应该是结构体还是类。一个十进制数+一个(int)枚举使其变成20字节。我认为还是用结构体比较好。 - nawfal
1
那个 Money NuGet 包的项目站点链接已经失效了,所以...没有文档? - George Mauer
问题在于,如果您正在创建自己的实现,您必须弄清楚如何实际持久化它。而且最受欢迎的ORM(EF)根本不支持自定义数据类型。因此,有人被要求深入研究,以完成本应该是非常简单的事情。 - George Mauer
1
@GeorgeMauer,Money包已经在保险库中了:https://github.com/danielcrenna/vault/tree/master/money - DotNetMatt

28

十进制。如果你选择双精度,那么你会面临舍入误差的风险。


10
double 可能会引入舍入误差,因为浮点数不能准确表示所有数字(例如,0.01 在浮点数中没有精确的表示)。另一方面,Decimal 可以 准确表示数字。(权衡是 Decimal 的范围比浮点数小)浮点数可能会给出意外的舍入误差(例如,0.01+0.01 != 0.02)。Decimal 可以产生舍入误差,但只有在你要求时才会产生(例如,Math.Round(0.01+0.02) 返回零)。 - Ian Boyd
2
@IanBoyd:值“$1.57”可以精确表示(double)157。如果使用double并在适当时候仔细应用缩放和特定于域的舍入,它可以完全精确。如果在舍入方面粗心大意,则decimal可能会产生语义上不正确的结果(例如,如果将多个应该四舍五入到最近的一分钱的值相加,但实际上没有先进行四舍五入)。decimal唯一好的地方是内置了缩放。 - supercat
1
@supercat,关于这个评论“如果将多个应该四舍五入到最近一分的值相加,但实际上没有先进行四舍五入”,我不明白为什么使用浮点数会解决这个问题。这是用户错误,与小数无关。我理解你的观点,但我觉得它被放错了位置,主要是因为IanBoyd已经指定了...如果你要求的话。 - sawe

18

15

支持货币模式:使用小数处理货币太过繁琐。

如果您创建一个货币类,那么可以把所有与货币相关的逻辑都放在那里,包括正确的ToString()方法、更好的控制解析值和更好的控制除法。

此外,使用货币类,不会意外地将货币与其他数据混淆。


12
另一个选项(特别是如果您正在自己创建类)是使用int或int64,并将低四位数字(甚至可能是2位)指定为“小数点右侧”。因此,“在边缘”处,您需要在输入时进行一些“* 10000”,并在输出时进行一些“/ 10000”。这是Microsoft SQL Server使用的存储机制,请参见http://msdn.microsoft.com/en-au/library/ms179882.aspx
好处是所有的求和都可以使用(快速的)整数运算来完成。

我也喜欢这个想法,处理小单位更容易处理和四舍五入。只需在用户界面中将其格式化为十进制。它解决了其他问题,比如将价格(小数)与数量(整数)相乘以得到总价格时,有时(根据语言而定)两边的计算都会作为整数进行,结果将不正确。然后还有一个问题,即在欧洲,点号和逗号代表着相反的含义。 我见过一些情况,在字符串形式的数据传递时,例如Json或Sql插入等,当值为$1,000时,被翻译为$1.00。 - userSteve
1
@userSteve - 是的,这是因为法国人(和其他人)使用“,”作为小数分隔符,而某些解析器过于宽容,可能将“,”和“.”都视为小数点。 - dsz

8
我曾经使用的大多数应用程序都使用decimal来表示货币。这是基于一个假设,即该应用程序永远不会涉及到超过一种货币。这个假设可能是基于另一个假设,即该应用程序永远不会在其他国家使用不同的货币。我见过这种假设被证明是错误的情况。
现在,这个假设正在以一种新的方式受到挑战:像比特币这样的新货币变得越来越普遍,而且它们不属于任何一个国家。一个仅在一个国家使用的应用程序可能仍然需要支持多种货币,这并不是不切实际的。
有些人会说,为货币创建甚至使用类型是“镀金”的,或者是在已知要求之外添加额外的复杂性。我强烈反对这种说法。在您的领域中,一个概念越普遍,就越重要采取合理的方法来正确地使用抽象。如果你想看到复杂性,请尝试在一个曾经使用decimal的应用程序中工作,现在每个decimal属性旁边都有一个额外的Currency属性。
如果你一开始使用了错误的抽象,以后替换它将会是一百倍的工作量。这意味着可能会在现有代码中引入缺陷,而最好的部分是,这些缺陷可能涉及金额、货币交易或任何与货币有关的事情。
使用除了decimal之外的东西并不难。谷歌“nuget money type”,你会看到许多开发人员已经创建了这样的抽象(包括我自己)。这很容易。就像使用DateTime而不是在string中存储日期一样容易。

5
创建自己的类。这似乎很奇怪,但是.Net类型无法涵盖不同的货币。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接