Java-это Bigdecimal.деление и округление
на работе мы обнаружили проблему при попытке разделить большое число на 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 в конце.
каков наилучший способ избежать такого поведения? Имейте в виду, что исходное число не контролируется мной.
5 ответов
как указано в javadoc, a BigDecimal определяется целочисленным значением и масштаб.
таким образом, значение числа, представленного BigDecimal (unscaledValue × 10^(- масштаб)).
так BigDecimal("1761e+5") имеет масштаб -5 и BigDecimal(176100000) имеет масштаб 0.
разделение двух BigDecimal выполняется с использованием шкал -5 и 0 соответственно, потому что шкалы не указаны при делении. The divide документация объясняет, почему результаты разные.
dividepublic 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) вы получите тот же результат.
выражения new BigDecimal("176100000") и new BigDecimal("1761e+5") are не равно. BigDecimal отслеживает как значение, так и точность.
BigDecimal("176100000") имеет 9 цифр точности и представляется внутренне как BigInteger("176100000"), умноженное на 1. BigDecimal("1761e+5") имеет 4 цифры точности и представляется внутренне как BigInteger("1761"), умноженное на 100000.
когда вы разделяете BigDecimal значением, результат уважает числа точности, приводящ в различных выходах для казалось бы, равные ценности.
Да, это своего рода проблема, что вы экспериментируете. Если позволите, в ситуации, когда у вас есть только экспоненциальные числа, вы должны бросить их, а затем использовать свой метод. Смотрите, что я предлагаю, это этот бит кода там:
long longValue = Double.valueOf("1761e+5").longValue();
BigDecimal value= new BigDecimal(longValue);
используйте его в методе, который преобразует эту строку в новую BigDecimal и возвращает это BigDecimal значение. Затем вы можете использовать эти возвращенные значения с divideBy1000.Это должно прояснить все ваши проблемы.
Если у вас много из них, что вы можете сделать также в хранилище тех BigDecimal в структуре данных, как список. Затем используйте цикл foreach, в котором вы применяете divideBy1000, и каждое новое значение будет храниться в другом списке. Тогда вам просто нужно получить доступ к этому списку, чтобы иметь новый набор значений !
надеюсь, что это помогает :)
попробуйте использовать 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
что вы хотите. (:
каждый раз, когда вы умножаете BigDecimal на степень 10, в этом случае вы умножаете на 10-3, вы можете использовать dividendo.scaleByPowerOfTen(power) который только изменяет масштаб объекта BigDecimal и боковые шаги любых проблем округления или, по крайней мере, перемещает их в более поздний расчет.
другие ответы здесь, охватывают более общий случай деления на любое число.