2016-12-24 6 views
1

Я пытаюсь использовать BigDecimal.pow(int i) с очень большой базой и экспонентами, однако я получаю ошибку ArithmeticException: Underflow.Возможные решения ошибки BigDecimal underflow

Чтобы просто поставить его, код:

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

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

Да, это проблема проекта Эйлера. Однако я знаю, что мои цифры верны. Это не математическая проблема, я просто не понимаю, почему BigDecimal.pow(int i) дает мне ArithmeticException: Underflow.

Я знаю, что 's scale is a 32-bit 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 

Спасибо.

+3

' BigDecimal.valueOf (2147483645.4141948) '- нет! Никогда не создавайте BigDecimal из литерала с плавающей запятой; вы уже сталкиваетесь с ошибкой округления, просто делая это. Создайте его из строкового литерала: 'new BigDecimal (" 2147483645.4141948 ")'. – user2357112

+0

@ user2357112 'BigDecimal.valueOf()' принимает 'long l' как свой аргумент, а не строковый литерал. – kkmonlee

+0

Упс, исправлено. Вам нужен конструктор, а не valueOf. – user2357112

ответ

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))); 

Я использую трюк от ответа Антона Довженко и тот факт, что 987654321 является 2601 * 379721. Расчет занимает около 4 секунд на моем компьютере, это, вероятно, сильно изменится.

С нетерпением жду ваших последующих вопросов.

EDIT: Центральная часть расчета может быть сделано как с простым кодом и быстрее, используя BigInteger вместо BigDecimal: (. Она печатает 11234048, как мы теперь знаем, что это должно)

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

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 значения займет много времени

Смежные вопросы