首页 文章

在BigDecimal.divide期间抛出ArithmeticException

提问于
浏览
29

我认为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?

我希望 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中明确记录了此行为:

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

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

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

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

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

9 回答

  • 1

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

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

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

    BigDecimal javadoc说:

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

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

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

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

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

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

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

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


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

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

  • 2

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

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

  • 21

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

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

    a/b + c
    

    等于

    (a + bc) / b.
    
  • 0

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

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

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

  • 7

    是否需要

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

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

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

  • 4

    您应该更喜欢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的主要用例,你会在那里找到它 .

  • 5

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

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

  • 2

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

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

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

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

    Best way to represent a fraction in Java?

  • 2

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

    我知道 1 / 30.333333... 并且应该可以将它存储在ram中,如"one divided by three"然后你可以将它再乘以你应该有 1 . 但我不认为Java有这样的东西......
    也许你必须为写这样的东西赢得诺贝尔奖 . ;-)

相关问题