[英]Possible solutions to BigDecimal underflow error
I am trying to use the BigDecimal.pow(int i)
with very big base and exponents, however I'm getting an ArithmeticException: Underflow
error. 我正在尝试将
BigDecimal.pow(int i)
与非常大的基数和指数一起使用,但是我遇到了ArithmeticException: Underflow
错误。
To simply put it, the code is: 简单地说,代码是:
BigDecimal base = BigDecimal.valueOf(2147483645.4141948);
BigDecimal product = base.pow(987654321);
System.out.println("product = " + product.toPlainString());
Yes, this is a Project Euler problem. 是的,这是欧拉计画的问题。 However I know my numbers are correct.
但是我知道我的数字是正确的。 This is not a mathematical problem, it is purely me not understanding why
BigDecimal.pow(int i)
is giving me an ArithmeticException: Underflow
. 这不是一个数学问题,纯粹是我不理解为什么
BigDecimal.pow(int i)
给我一个ArithmeticException: Underflow
。
I know that BigDecimal
's scale
is a 32-bit int
but is there any way at all to bypass this and calculate such a big value? 我知道
BigDecimal
的scale
位数是32位int
但是有没有办法绕过它并计算这么大的值? If it helps, I do plan on flooring the product and modding it by 100000000
since I only want the last 8 digits. 如果有帮助,我确实计划将产品铺地板并进行
100000000
修改,因为我只希望最后8位数字。 If there is any other way I could do this mathematically, I'd like a hint. 如果还有其他方法可以在数学上做到这一点,请给我一个提示。
Stack trace: 堆栈跟踪:
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
Thanks. 谢谢。
Computation could be broken in several parts, for example: 计算可以分为几个部分,例如:
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
is thrown because scaleValue * powValue
is outside [Integer.MIN_VALUE; Integer.MAX_VALUE]
因为
scaleValue * powValue
在[Integer.MIN_VALUE; Integer.MAX_VALUE]
之外[Integer.MIN_VALUE; Integer.MAX_VALUE]
所以抛出ArithmeticException
[Integer.MIN_VALUE; Integer.MAX_VALUE]
[Integer.MIN_VALUE; Integer.MAX_VALUE]
segment. [Integer.MIN_VALUE; Integer.MAX_VALUE]
段。 Note, that resetting of scale after applying pow
is necessary, because BigDecimal
scale is recalculated every time pow
is invoked and equal to oldScaleValue * powValue
请注意,应用
pow
之后必须重新设置比例,因为每次调用pow
都会重新计算BigDecimal
比例,该比例等于oldScaleValue * powValue
Also, I assume, that getting pow value will take much time 而且,我认为获得战利品价值将花费很多时间
The answer is a decimal number with 6913580247 decimals ending in "11234048" (the last 8 decimals). 答案是一个十进制数,其中以6913580247的十进制数结尾为“ 11234048”(最后8个十进制数)。 You have 7 decimals in your base, and 987654321 * 7 equals 6913580247.
您的基数中有7位小数,而987654321 * 7等于6913580247。
My problem is this number cannot be represented in a BigDecimal
because it would need a scale of 6913580247, which overflows the integer that BigDecimal
uses for its scale. 我的问题是,该数字不能用
BigDecimal
表示,因为它需要小数位数为6913580247,这会使BigDecimal
用于其小数位数的整数溢出。 I don't know in which format you want your number instead. 我不知道您要使用哪种格式。 The following code prints out the result as
以下代码将结果打印为
Result is 1.1234048e-6913580240
That is, like scientific notation, only with an exponent out of the normal range for scientific notation. 就是说,就像科学记数法一样,只有指数超出了科学记数法的正常范围。 For the modulo 100000000 I am using:
对于模100000000,我正在使用:
public static final BigDecimal moduloBase = new BigDecimal(10).pow(8); // 8 digits
Now I do: 现在我做:
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)));
I am using the trick from Anton Dovzhenko's answer and the fact that 987654321 is 2601 * 379721. The calculation takes some 4 seconds on my computer, this will probably vary a great deal. 我正在使用安东·多夫任科(Anton Dovzhenko)的回答的技巧,以及987654321为2601 * 379721的事实。计算在我的计算机上花费了大约4秒钟,这可能相差很大。
Looking forward to your follow-up questions. 期待您的后续问题。
EDIT: The central part of the calculation can be done both with simpler code and faster using BigInteger
instead of BigDecimal
: 编辑:使用
BigInteger
而不是BigDecimal
可以用更简单的代码和更快的速度完成计算的中心部分:
BigInteger bi = new BigInteger("54141948");
bi = bi.modPow(new BigInteger("987654321"), new BigInteger("100000000"));
System.out.println("As BigInteger: " + bi);
(It prints 11234048
as we now know it should.) (如我们现在所知,它会打印
11234048
)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.