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