1

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

简单地说,代码是:

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

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

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

我知道BigDecimal'sscale是 32 位的int,但是有什么办法可以绕过它并计算出这么大的值吗?如果有帮助,我确实计划为产品铺地板并对其进行改装,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

谢谢。

4

2 回答 2

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

另外,我认为,获得 pow 价值将需要很多时间

于 2016-12-24T04:53:44.583 回答
1

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

我的问题是这个数字不能用 a 表示,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(我们现在知道它应该打印出来。)

于 2017-01-09T07:57:26.137 回答