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 в конце.
Каков наилучший способ избежать такого поведения? Имейте в виду, что исходное число меня не контролирует.
Ответы
Ответ 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 и боковые шаги, любые проблемы округления, или, по крайней мере, переводит их на более поздний расчет.
Другие ответы здесь касаются более общего случая деления на любое число.