用Java表示货币值[关闭]


94

我了解建议使用BigDecimal来表示Java中的货币值的最佳实践。你用什么?您是否有更喜欢使用的更好的库?


4
看看JSR 354
yegor256

1
这是一个您可以复制和扩展的货币类:java-articles.info/articles/?p=254
Gilbert Le

另请参见JSR-354的参考实现github.com/JavaMoney/jsr354-ri

Answers:


81

BigDecimal一路走来。我听说有些人创建自己的类CashMoney用货币封装现金值的类,但在表皮下仍然是BigDecimal,可能是BigDecimal.ROUND_HALF_EVEN四舍五入。

编辑:正如唐在他的回答中提到的那样,有timeandmoney之类的开源项目,尽管我赞扬它们试图防止开发人员不得不重新发明轮子,但我对使用pre-alpha库没有足够的信心它在生产环境中。此外,如果您在引擎盖下四处挖掘,也会发现它们也使用BigDecimal


4
+1。我们还决定添加一个消耗货币的容器类。在表中呈现货币值时,这很方便。
丹尼尔·希勒

1
是的,这是一种非常普遍的方法,而且很有意义。一个需要注意的问题是,当您必须处理日元时,因为它们没有像美分这样的次要货币,因此它需要自己的取整规则。
08年

3
@nineside举了一个很好的例子,说明为什么滚动自己的答案不好。“哦,顺便说一句,它不适用于$ CURRENCY_X。” 这是一个很好的信号,它也不适用于许多其他货币。
James Moore

1
@JamesMoore我不同意“按自己的方式做”是一种不好的方法,您只需要知道选择的方法可能存在的局限性,因此我提起它是有原因的。每种货币实施不同的取整规则很简单,但是如果您的系统只需要处理美元或欧元,那么您就不需要过度设计。
双面2011年

1
看看stackoverflow.com/questions/5134237/…仅仅是BigDecimal出现问题的一个原因。全球范围内的会计只是大量特殊情况,而试图在BigDecimal的帮助下将它们全部清除是行不通的。
詹姆斯·摩尔



8

我之前遇到的一个方便的库是Joda-Money库。实际上,其实现之一是基于BigDecimal。它基于ISO-4217货币规范,并且可以支持自定义货币列表(通过CVS加载)。

该库中有少量文件,如果需要修改,可以快速浏览。Joda-Money是根据Apache 2.0许可发布的。


7

如果您仅使用美元和美分,我将使用多头(抵消2个小数位)。如果需要更多详细信息,可以选择大十进制。

无论哪种方式,我都可能将类扩展为具有使用正确格式的.toString(),并作为放置其他可能出现的其他方法的地方(长期以来,如果十进制为小数,则乘法和除法会出错没有调整)

另外,如果使用定义自己的类和接口,则可以随意替换实现。


2
提防,甚至太长的时间也可能太短而无法在美分中持有美国联邦债务……如果不是现在,那么再过几年。
Ingo 2013年

3
我同意-大量美元(或者如果您以日元为单位跟踪货币),则应该使用BigDecimal-但即使如此,我还是会认真考虑使用容器类。我认为大多数编程复杂性来自于人们没有围绕集合和内在类型定义小的简单类。
Bill K

3

BigDecimal 或其他定点表示形式通常是金钱所需要的。

浮点(DoubleFloat)表示和计算是不精确的,从而导致错误的结果。


7
严格来说,BigDecimal也不精确;它恰好与我们日常生活中使用的十进制舍入更好地对应,并且允许您指定舍入模式。
Michael Borgwardt

1
@Michael Borgwardt BigDecimal与IEEE FP的不同之处在于指定了显式比例。尽管并非所有操作都是精确的,但这可以确保一组操作和行为始终是准确的,并且比例是恒定的,而IEEE FP 的比例则随值而减小。

1
那和钱有什么关系?世界各地的会计组织通常对如何使用其货币进行数学运算有非常特定的要求。BigDecimal是否与这些标准中的每一个精确匹配?当这些标准改变时,明年会这样做吗?而且BigDecimal甚至没有接近为货币指定有用的舍入规则。
James Moore

2

在处理时间和金钱时,您必须非常小心。

希望您在赚钱时,每个人都应该知道永远不要使用浮动或双倍。

但是我不确定BigDecimal。

在大多数情况下,如果您只跟踪整数或整数,就可以了。这样,您永远不会处理小数位。

打印时只显示美元。始终使用内部整数使用美分。如果需要除法或需要使用Math.abs(),这可能会很棘手。

但是,您可能只关心百分之一,甚至百分之一。我不知道这样做的好方法。您可能只需要处理千分之一美分并使用很长时间。否则,您可能会被迫使用BigDecimal

我会在这方面做更多的阅读,但请忽略开始谈论使用浮点数或双精度表示货币的每个人。他们只是在找麻烦。

我觉得我的建议还不完善,因此请多加建议。您正在处理危险类型!


2
为什么需要“强制”使用BigDecimal?你不确定什么 它明显优于使用cents,因为它允许您显式指定舍入模式。
Michael Borgwardt

1
@MichaelBorgwardt:是的,它允许您指定货币所需的舍入模式的一小部分。所以?(提示:四舍五入的货币通常是由国家会计机构决定的。它们非常乐于在奇怪的特殊情况下使用。请参阅stackoverflow.com/questions/5134237/…,因为BigDecimal舍入完全是许多有趣的原因之一没用。)
詹姆斯·摩尔

@James:“无用”到底是什么?使用BigDecimal比使用其他方法更难实现特殊情况?
Michael Borgwardt

1
好吧,完全没用的太强大了。在抽象货币的复杂类中,BigDecimal的舍入规则在某些特定实例中可能有用,以构建货币舍入发生方式的子集。但是一般情况是,货币的四舍五入规则要求机制会随着时间的流逝而变化(因为人力会计机构制定了规则,可以自由地对其进行更改)。问题不是关于欧元(或下月取代欧元的任何东西……),也不是关于2011年的美元,而是关于货币,因此您必须处理很多令人讨厌的复杂性。
詹姆斯·摩尔

2

创建Money类是必经之路。在下面使用BigDecimal(甚至是int)。然后使用Currency类定义舍入约定。

不幸的是,如果没有运算符重载,Java会使创建此类基本类型变得非常不愉快。


2

有一个更好的图书馆,时间和金钱。IMO,它远远超过了JDK提供的代表这两个概念的库。


3
这个答案是三年前发布的。今天,根据该链接,timeandmoney项目仍处于预测试阶段。
James Moore

1
@JamesMoore好的电话。现在的答案是7年,项目仍然不稳定。
纳文2015年

1

绝对不是BigDecimal。舍入和表示有很多特殊规则,您需要担心。

马丁·福勒(Martin Fowler)建议实现专用的Money类来表示货币金额,并且还应实现货币换算的规则。


6
和他的Money类的基础数据类型?BigDecimal。
08年

1
这不是真的。您可以在货币类中使用Integer,这是Martin所做的。我已经做了很多次了。
egervari 2011年

该建议是正确的。涉及金钱的计算是随时间变化的大量特殊情况。BigDecimal可能作为解决方案的一小部分有用,但肯定不是一般的。
詹姆斯·摩尔

1

嘿,这是一篇有关BigDecimal的非常有趣的文章,并举例说明了为什么有时使用它而不是使用double。BigDecimal教程


0

最终显示货币值时,可以使用DecimalFormat类。它提供了本地化支持,并且可以扩展。


0

我将BigDecimal封装在Money类中,该类也具有一种货币,就像上面提到的那样。重要的是您要进行大量的单元测试,尤其是在使用其他货币时。如果添加一个方便的构造函数(采用字符串或工厂方法执行相同操作),以便编写如下所示的测试,这也是一个好主意:

   assertEquals(Money.create("100.0 USD").add("10 GBP"),Money.create("116 USD"));

0

总会涉及到约束和细节。任何没有足够经验的人都不能理解下一篇文章中概述的细微问题,在处理实际财务数据之前,应该认真考虑一下:

http://lemnik.wordpress.com/2011/03/25/bigdecimal-and-your-money

BigDecimal几乎不是唯一正确的表示形式或难题的唯一组成部分。在特定条件下,使用以美分作为整数存储的Money类就足够了,并且比BigDecimal快得多。是的,这意味着要使用美元作为货币和限额,但是这种约束对于许多用例是完全可以接受的,而且所有货币对于舍入和子面额都有特殊情况,因此没有“通用”解决方案。


1
这似乎是对其他帖子的评论,而不是实际答案。它也有过多的硫酸。请尝试将来变得更加文明。
Slater Victoroff
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.