在BigDecimal.divide期间抛出ArithmeticException

时间:2010-05-01 09:03:07

标签: java precision division bigdecimal financial

我认为java.math.BigDecimal应该是需要用十进制数执行无限精度算术的答案。

请考虑以下代码段:

import java.math.BigDecimal;
//...

final BigDecimal one = BigDecimal.ONE;
final BigDecimal three = BigDecimal.valueOf(3);
final BigDecimal third = one.divide(three);

assert third.multiply(three).equals(one); // this should pass, right?

我希望assert能够通过,但事实上执行甚至没有到达:one.divide(three)导致ArithmeticException被抛出!

Exception in thread "main" java.lang.ArithmeticException:
Non-terminating decimal expansion; no exact representable decimal result.
    at java.math.BigDecimal.divide

事实证明,API

中明确记录了此行为
  

divide的情况下,确切的商可以具有无限长的十进制扩展;例如,1除以3.如果商具有非终止十进制扩展并且指定了操作以返回精确结果,则抛出ArithmeticException。否则,将返回除法的确切结果,与其他操作一样。

进一步浏览API,发现实际上有divide的各种重载执行不精确的除法,即:

final BigDecimal third = one.divide(three, 33, RoundingMode.DOWN);
System.out.println(three.multiply(third));
// prints "0.999999999999999999999999999999999"

当然,现在显而易见的问题是“有什么意义?”。当我们需要完全算术时,我认为BigDecimal是解决方案,例如用于财务计算。如果我们不能确切地divide,那么这有多大用处呢?它实际上是用于一般目的,还是仅在非常适合的应用中有用,幸运的是,您根本不需要divide

如果这不是正确答案,我们在财务计算中使用什么 CAN 进行精确划分? (我的意思是,我没有金融专业,但他们仍然使用分工,对吧???)。

9 个答案:

答案 0 :(得分:22)

  

如果这不是正确的答案,我们可以使用什么来进行财务计算中的精确划分? (我的意思是,我没有金融专业,但他们仍然使用分工,对吧???)。

然后我在小学 1 ,他们告诉我,当你除以1乘3时,得到0.33333 ...即重复小数。以十进制形式表示的数字划分并不准确。事实上,对于任何固定的 base ,都会有一些分数(将一个整数除以另一个整数的结果)无法将完全表示为该基数中的有限精度浮点数。 (这个数字会有一个反复出现的部分......)

当您进行涉及除法的财务计算时,您考虑如何处理经常性的部分。你可以将它向上或向下舍入,或者到最近的整数或其他东西,但基本上你不能忘记这个问题。

BigDecimal javadoc说:

  

BigDecimal类使用户可以完全控制舍入行为。如果未指定舍入模式且无法表示确切结果,则抛出异常;否则,通过向操作提供适当的MathContext对象,可以执行计算到选定的精度和舍入模式。

换句话说,告诉BigDecimal如何处理舍入是你的责任

编辑 - 响应OP的这些后续行动。

  

BigDecimal如何检测无限重复的小数?

它没有明确检测到重复的小数。它只是检测到某些操作的结果无法使用指定的精度精确表示;例如精确表示的小数点后需要太多数字。

  

必须跟踪并检测红利中的周期。它可能已经选择以另一种方式处理,通过标记重复部分的位置等等。

我认为可以指定BigDecimal来精确地表示重复的小数;即作为BigRational类。但是,这会使实现更复杂,使用 2 更昂贵。并且由于大多数人都希望数字以十进制显示,并且在此时重复出现十进制问题。

最重要的是,这种额外的复杂性和运行时成本不适合BigDecimal的典型用例。这包括财务计算,其中会计惯例不允许您使用重复小数。


1 - 这是一所优秀的小学......

2 - 要么你试图去除除数和被除数的共同因子(计算成本很高),要么允许它们无限制地增长(空间使用昂贵......并且计算后用于以后的操作)。

答案 1 :(得分:7)

该课程为BigDecimal而不是BigFractional。从你的一些评论中听起来你只是想抱怨有人没有在这个类中构建所有可能的数字处理算法。财务应用程序不需要无限小数精度;只需要精确到所需精度的值(通常为0,2,4或5个十进制数字)。

实际上我处理过许多使用double的财务应用程序。我不喜欢它,但这就是它们的编写方式(也不是Java)。当存在汇率和单位转换时,存在四舍五入和瘀伤问题的可能性。 BigDecimal消除了后者,但仍有前者用于分裂。

答案 2 :(得分:5)

如果您想使用小数,而不是有理数,并且在最终舍入之前需要精确的算术(四舍五入到美分或其他),这里有一个小技巧。

您可以随时操作公式,以便只有一个最终分区。这样你在计算过程中就不会失去精确度,并且你总能获得正确的舍入结果。例如

a/b + c

等于

(a + bc) / b.

答案 3 :(得分:4)

  

顺便说一句,我真的很感激   来自与之合作过的人的见解   财务软件。我经常听到   BigDecimal主张超过双重

在财务报告中,我们使用具有scale = 2和ROUND_HALF_UP的alwasy BigDecimal,因为报告中的所有打印值必须导致可重现的结果。如果有人使用简单的计算器进行检查。

在瑞士,由于他们不再拥有1或2枚Rappen硬币,因此它们会变为0.05。

答案 4 :(得分:2)

是否需要

a=1/3;
b=a*3;

resulting in

b==1;

在金融系统?我猜不会。在金融系统中,它被定义,在进行计算时必须使用哪种圆模和比例。在某些情况下,圆形模式和比例在法律中定义。所有组件都可以依赖于这种定义的行为。返回b == 1将失败,因为它不会满足指定的行为。这在计算价格等时非常重要。

它类似于IEEE 754规范,用于表示二进制数字的浮点数。组件不得在不丢失信息的情况下优化“更好”的表示,因为这会破坏合同。

答案 5 :(得分:2)

您应该更喜欢BigDecimal进行财务计算。舍入应由业务指定。例如。金额(100,00美元)必须在三个账户中平均分配。必须有一个业务规则,该账户需要额外的分数。

Double,浮点数并不适用于金融应用程序,因为它们不能精确地表示不是2的指数的1的分数。考虑0.6 = 6/10 = 1 * 1/2 + 0 * 1/4 + 0 * 1/8 + 1 * 1/16 + ... = 0.1001 ... b

对于数学计算,您可以使用符号数字,例如存储分母和分子甚至整个表达式(例如,这个数字是sqrt(5)+3/4)。因为这不是java api的主要用例,所以你会在那里找到它。

答案 6 :(得分:2)

要划分保存,您必须设置MATHcontext

BigDecimal bd = new BigDecimal(12.12, MathContext.DECIMAL32).divide(new BigDecimal(2)).setScale(2, RoundingMode.HALF_UP);

答案 7 :(得分:1)

我接受Java对表示分数没有很大的支持,但是你必须意识到在使用计算机时保持完全精确是不可能。至少在这种情况下,例外是告诉你精度正在丢失。

据我所知,“无限精度算术与十进制数”不会发生。如果你必须使用小数,你正在做的事情可能很好,只是捕获例外。否则,快速谷歌搜索会找到一些有趣的资源来处理Java中的分数:

http://commons.apache.org/math/userguide/fraction.html

http://www.merriampark.com/fractions.htm

Best way to represent a fraction in Java?

答案 8 :(得分:0)

注意我们正在使用一台计算机......一台计算机有很多内存和精度需要内存。因此,当您需要无限精度时,您需要使用 (infinite * infinite) ^ (infinite * Integer.MAX_VALUE) terrabyte ram ......

我知道1 / 30.333333...,应该可以将其存储在ram中,例如“一分为三”,然后你可以将其相乘,你应该1。但我不认为Java有这样的东西......
也许你必须为写这样的东西赢得诺贝尔奖。 ;-)