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

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