在BigDecimal.divide期间抛出ArithmeticException

pol*_*nts 33 java precision division bigdecimal financial

我认为java.math.BigDecimal应该是The Answer™需要用十进制数执行无限精度算术.

请考虑以下代码段:

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?
Run Code Online (Sandbox Code Playgroud)

我希望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
Run Code Online (Sandbox Code Playgroud)

事实证明,API中明确记录了此行为:

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

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

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

当然,现在显而易见的问题是"有什么意义?".BigDecimal当我们需要精确算术时,我认为是解决方案,例如用于财务计算.如果我们甚至不能divide确切地说,那么这有多大用处呢?它实际上是用于一般目的,还是只在一个非常小的应用程序中有用,幸运的是你根本不需要它divide

如果这不是正确的答案,什么CAN我们使用的财务核算准确划分的?(我的意思是,我没有金融专业,但他们仍然使用师,对吧?).

Ste*_*n C 23

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

然后我在小学1,他们告诉我,当你除以1乘3时,得到0.33333 ...即重复的小数.以十进制形式表示的数字划分并不准确.实际上对于任何固定基础将存在不能表示级分(将一个整数由另一个的结果)恰好为一个有限精度浮点在于基点数.(这个数字会有一个反复出现的部分......)

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

BigDecimal javadoc说:

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

换句话说,您有责任告诉BigDecimal如何处理舍入.

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

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

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

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

我想这BigDecimal可能已被指定为完全代表重复的小数; 即作为一个BigRational阶级.但是,这会使实现更复杂,使用更昂贵2.并且由于大多数人都希望数字以十进制显示,并且在此时重复出现十进制问题.

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


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

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


Kev*_*ock 9

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

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

  • +1.你用"BigDecimal"而不是"BigFractional"将它钉在了正确的位置.您对金融应用的"双重"的见解表示赞赏. (2认同)

COM*_*ROM 6

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

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

a/b + c
Run Code Online (Sandbox Code Playgroud)

等于

(a + bc) / b.
Run Code Online (Sandbox Code Playgroud)


sta*_*ker 5

顺便说一句,我非常感谢那些使用过财务软件的人的见识。我经常听到BigDecimal被提倡加倍

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

在瑞士,由于不再具有1或2个Rappen硬币,它们舍入为0.05 。