C#中处理货币金额的最佳实践

9

我已经阅读了一些关于使用特定数据类型存储货币金额的风险的文章。不幸的是,其中一些概念超出了我的理解范畴。

在阅读这些文章后,有没有对在C#中处理货币的最佳实践和建议?应该使用哪种数据类型来处理小额和大额货币?此外,我位于英国,这意味着我们使用逗号(例如£4,000,而其他文化以不同的方式表示相同的金额)。

7个回答

16

对于货币金额来说,Decimal类型是最明智的选择。

Decimal是一种基于10进制的浮点数类型,具有28+位小数位精度。使用Decimal类型,您会遇到比使用基于2进制的Double类型更少的意外情况。

Double类型使用的内存仅为Decimal类型的一半,并且由于CPU硬件支持,Double在许多常见浮点运算中速度更快。但它无法准确表示大多数基于10进制的分数(例如1.05),并且只有15+位小数位精度。然而,Double拥有更大的范围(可以表示更大和更小的数字),这在某些计算,尤其是某些统计计算中非常有用。

您问题的一个回答声明Decimal是固定小数点类型,小数部分有4位。 这并不正确。如果您怀疑这一点,请注意以下代码行生成的结果为0.0000000001:

Console.WriteLine("number={0}", 1m / 10000000000m);

尽管这么说,有趣的是,全球最广泛使用于货币金额计算的软件,Microsoft Excel,使用的是double数据类型。当然,他们必须要做很多额外处理来使它能够正常运作,但仍有些问题存在。在Excel中尝试以下两个公式:

  • =1-0.9-0.1
  • =(1-0.9-0.1)

第一个公式得到的结果为0,而第二个则得到了约为-2.77e-17的值。实际上,Excel在某些情况下在对数字进行加减运算时会进行一些修正,但并非所有情况都如此。


货币计算使用小数是不错的,但货币比率仍应该是双倍的(例如利率)。 - Richard

8

由于浮点数的舍入误差,您不应使用浮点数。使用十进制类型(decimal)更为合适。


1
我点赞只是为了抵消那个踩的。我认为在没有评论的情况下不应该允许踩。 - Jeff Reddy

6

Martin Fowler建议使用一个Money类。请查看链接以了解原理。有许多实现他想法的方法,或者您可以自己编写。Fowler自己的实现是用Java编写的,因此他使用了一个类。我见过的C#版本使用结构体,这似乎很合理。


1
此链接是 404 :( - BoD

2

我使用一个值对象来保存金额(作为decimal)和货币。这使得可以同时处理不同的货币。在.NET中,decimal是用于货币的推荐数据类型。


1

无论你做什么,一定要确保你理解每个应用程序层中如何处理货币金额。

我曾经花了一周时间追踪一个1美分的错误,因为SQLServer和.Net使用不同的方式来舍入货币,而且应用程序在处理某些类型的计算时不一致 - 有时是在SQL中完成,有时是在.net中完成。如果您感兴趣,可以了解 "银行家舍入"。

还有与格式化货币相关的问题 - 不确定是否需要处理非英国金额、其他语言/文化等,但这将增加另一个复杂度级别。


1
另一个需要注意的问题是,SQL Server 以 3.33 毫秒(0.00333 秒)的精度存储 DATETIME。有一次我因此遭遇了问题,因为它对我的目的来说不足够精确。 - ahsteele

0

如果需要进行除法运算,我的建议是使用 Decimal,正如其他人所推荐的那样。对于简单的计数应用程序,我建议使用 Integer 类型。对于这两种类型,我总是在最低货币单位上工作(例如加拿大/美国的分)。

我喜欢 @dangph 添加的 Fowler's Money call 理论。


0

正如您在问题中所指出的,除了使用适当的数据类型之外,程序如何处理货币转换也非常重要。当然,这个问题不仅限于货币。Jeff Atwood发表了一篇很棒的文章,总结了执行The Turkey Test的优点。


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