BigDecimal下溢错误的可能解决方案

时间:2016-12-24 02:18:01

标签: java bigdecimal arithmeticexception

我正在尝试使用具有非常大的基数和指数的BigDecimal.pow(int i),但是我收到ArithmeticException: Underflow错误。

简单地说,代码是:

BigDecimal base = BigDecimal.valueOf(2147483645.4141948);
BigDecimal product = base.pow(987654321);

System.out.println("product = " + product.toPlainString());

是的,这是项目欧拉问题。但我知道我的数字是正确的。这不是一个数学问题,纯粹是我不明白为什么BigDecimal.pow(int i)给了我一个ArithmeticException: Underflow

我知道BigDecimal的{​​{3}}但是有什么方法可以绕过这个并计算出如此大的价值?如果它有帮助,我会计划对产品进行平铺并按100000000对其进行修改,因为我只想要最后8位数。如果还有其他任何方式我可以用数学方法做到这一点,我想要一个提示。

堆栈追踪:

Exception in thread "main" java.lang.ArithmeticException: Underflow
    at java.math.BigDecimal.checkScale(BigDecimal.java:3841)
    at java.math.BigDecimal.pow(BigDecimal.java:2013)
    at test.main(test.java:10)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)

Process finished with exit code 1

感谢。

2 个答案:

答案 0 :(得分:1)

计算可以分为几个部分,例如:

BigDecimal base = BigDecimal.valueOf(2147483645.4141948);
base = base.setScale(20, BigDecimal.ROUND_FLOOR);
// 109739369 = 6455257 * 17
base = base.pow(17).setScale(20, BigDecimal.ROUND_FLOOR);
base = base.pow(6455257);
抛出

ArithmeticException,因为scaleValue * powValue位于[Integer.MIN_VALUE; Integer.MAX_VALUE]段之外。请注意,在应用pow后重置比例是必要的,因为每次调用BigDecimal并且等于pow

时,都会重新计算oldScaleValue * powValue比例

另外,我认为,获得战俘价值需要很长时间

答案 1 :(得分:1)

答案是十进制数,小数位数为6913580247,以“11234048”结尾(最后8位小数)。你的基数有7位小数,987654321 * 7等于6913580247.

我的问题是这个数字无法在BigDecimal中表示,因为它需要6913580247的比例,它会溢出BigDecimal用于其比例的整数。我不知道您希望以哪种格式编号。以下代码将结果打印为

Result is 1.1234048e-6913580240

就像科学记数法一样,只有指数超出科学记数法的正常范围。对于我使用的模数100000000:

public static final BigDecimal moduloBase = new BigDecimal(10).pow(8); // 8 digits

现在我做了:

    long noOfDecimals = 987654321L * 7L;

    BigDecimal bd = new BigDecimal("54141948"); // last 8 digits of base
    bd = bd.pow(379721);
    bd = bd.remainder(moduloBase);
    bd = bd.pow(2601);
    bd = bd.remainder(moduloBase);

    double result = bd.doubleValue() / 10_000_000.0; // print with 7 decimals
    System.out.println("Result is " + result + "e" + (-(noOfDecimals - 7)));

我正在使用Anton Dovzhenko的回答以及987654321为2601 * 379721这一事实。计算机上的计算时间约为4秒,这可能会有很大差异。

期待您的后续问题。

编辑:计算的核心部分既可以使用更简单的代码,也可以使用BigInteger代替BigDecimal更快地完成:

    BigInteger bi = new BigInteger("54141948");
    bi = bi.modPow(new BigInteger("987654321"), new BigInteger("100000000"));
    System.out.println("As BigInteger: " + bi);

(它打印11234048,因为我们现在知道它应该。)