可能的解决方案,用于BigDecimal下流误差



我正在尝试使用具有很大的基础和指数的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

我知道BigDecimalscale是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

谢谢。

答案是十进制数字,其中6913580247小数为" 11234048"(最后8个小数)。您的基地中有7个小数,而987654321 * 7等于6913580247。

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

Result is 1.1234048e-6913580240

即,就像科学符号一样,只有一个指数超出了科学符号的正常范围。对于Modulo 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。)

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

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后的比例重置是必要的,因为每次调用powBigDecimal比例都会重新计算,并且等于oldScaleValue * powValue

我认为,获得POW值将花费大量时间

最新更新