Подтвердить что ты не робот

Java Bigdecimal.divide и округление

На работе мы обнаружили проблему при попытке делить большое число на 1000. Это число поступало из базы данных.

Скажем, у меня есть этот метод:

private static BigDecimal divideBy1000(BigDecimal dividendo) {
    if (dividendo == null) return null;

    return dividendo.divide(BigDecimal.valueOf(1000), RoundingMode.HALF_UP);
}

Когда я делаю следующий вызов

divideBy1000(new BigDecimal("176100000"))

Я получаю ожидаемое значение 176100. Но если я попробую строку ниже

divideBy1000(new BigDecimal("1761e+5"))

Получаю значение 200000. Почему это происходит? Оба числа одинаковы с другим представлением, и последнее - это то, что я получаю от базы данных. Я так понимаю, что JVM делит число 1761 на 1000, округляя и заполняя 0 в конце.

Каков наилучший способ избежать такого поведения? Имейте в виду, что исходное число меня не контролирует.

4b9b3361

Ответ 1

Как указано в javadoc, a BigDecimal определяется целым числом и шкалой.

Следовательно, значение числа, представленного BigDecimal, (unscaledValue × 10 ^ (- масштаб)).

Итак, BigDecimal("1761e+5") имеет масштаб -5 и BigDecimal(176100000) имеет шкалу 0.

Разделение двух BigDecimal выполняется с использованием шкал -5 и 0 соответственно, потому что шкалы не определены при делении. В документации divide объясняется, почему результаты разные.

divide

public BigDecimal divide(BigDecimal divisor)

Возвращает BigDecimal, значение которого (this / divisor), а предпочтительный масштаб которого равен (this.scale() - divisor.scale()); если точный коэффициент не может быть представлен (поскольку он имеет неограничивающее десятичное расширение), то бросается ArithmeticException.

Параметры:

divisor - значение, с помощью которого этот BigDecimal следует разделить.

Возврат:

this / divisor

Броски:

ArithmeticException - если точный коэффициент не имеет конечного десятичного расширения

С:

1,5

Если вы укажете масштаб при делении, например. dividendo.divide(BigDecimal.valueOf(1000), 0, RoundingMode.HALF_UP) вы получите тот же результат.

Ответ 2

Выражения new BigDecimal("176100000") и new BigDecimal("1761e+5") не равны. BigDecimal отслеживает как значение, так и точность.

BigDecimal("176100000") имеет 9 цифр точности и представлен внутренне как BigInteger("176100000"), умноженный на 1. BigDecimal("1761e+5") имеет 4 цифры точности и представлен внутренне как BigInteger("1761"), умноженный на 100000.

Когда вы делите a BigDecimal на значение, результат соответствует цифрам точности, что приводит к разным выходам для кажущихся равными.

Ответ 3

Да, такой вопрос, что вы экспериментируете. Если я могу, в ситуации, когда у вас есть только экспоненциальные числа, вы должны бросить их, а затем использовать свой метод. Посмотрите, что я предлагаю, вот этот бит кода:

long  longValue = Double.valueOf("1761e+5").longValue();
BigDecimal value= new BigDecimal(longValue);

Используйте его в методе, который преобразует эти строки в новый BigDecimal и возвращает это значение BigDecimal. Затем вы можете использовать эти возвращаемые значения с помощью divideBy1000. Это должно очистить любую проблему, которую вы имеете.

Если у вас их много, то вы можете также сохранить эти BigDecimal в структуре данных, например, в списке. Затем используйте цикл foreach, в котором вы применяете divideBy1000, и каждое новое значение будет храниться в другом списке. Тогда вам просто нужно получить доступ к этому списку, чтобы получить новый набор значений!

Надеюсь, это поможет:)

Ответ 4

Попробуйте использовать round().

private static BigDecimal divideBy1000(BigDecimal dividendo) {
    if (dividendo == null) return null;

    return dividendo.divide(BigDecimal.valueOf(1000)).round(new MathContext(4, RoundingMode.HALF_UP));
}

 public static void main(String []args){
    BigDecimal bigD = new BigDecimal("1761e5");
    BigDecimal bigDr = divideBy1000(bigD);
    System.out.println(bigDr);
 }

Строка new MathContext(4, RoundingMode.HALF_UP)) возвращает деление на 4 места.

Это дает:

1.761E+5

Это то, что вы хотите. (

Ответ 5

При умножении BigDecimal на мощность 10, в этом случае вы умножаетесь на 10 -3 вы можете использовать dividendo.scaleByPowerOfTen(power), который только изменяет масштаб объекта BigDecimal и боковые шаги, любые проблемы округления, или, по крайней мере, переводит их на более поздний расчет.

Другие ответы здесь касаются более общего случая деления на любое число.