BigDecimal compareTo работает не так, как ожидалось

#java #bigdecimal

#java #bigdecimal

Вопрос:

Согласно JavaDoc для BigDecimal , compareTo функция не учитывает масштаб при сравнении.

Теперь у меня есть тестовый пример, который выглядит примерно так:

 BigDecimal result = callSomeService(foo);
assertTrue(result.compareTo(new BigDecimal(0.7)) == 0); //this does not work
assertTrue(result.equals(new BigDecimal(0.7).setScale(10, BigDecimal.ROUND_HALF_UP))); //this works
  

Значение, которое я ожидаю вернуть от функции, равно 0.7 и имеет масштаб 10. Вывод значения показывает мне ожидаемый результат. Но compareTo() функция, похоже, работает не так, как я думаю, она должна.

Что здесь происходит?

Комментарии:

1. Вы ожидаете , что значение будет равно 0.7, но уверены ли вы? Вы пробовали заставить callSomeService просто возвращать новый BigDecimal (0.7)?

2. @CPerkins Да, совершенно уверен в этом. Я напечатал результат, и equals метод также работал. В любом случае, проблема решена 🙂

3. Это тоже озадачивало меня, пока я не понял, что я создавал BigDecimal из DOUBLE… :-

Ответ №1:

new BigDecimal(0.7) не соответствует 0.7.

Оно представляет 0,69999999999999555910790149937383830547332763671875 (точно).

Причина этого в том, что double литерал 0.7 не соответствует 0.7 в точности.

Если вам нужны точные BigDecimal значения, вы должны использовать String конструктор (фактически, будут работать все конструкторы, которые не принимают double значения).

Попробуйте new BigDecimal("0.7") вместо этого.

В JavaDoc BigDecimal(double) конструктора есть некоторые связанные примечания:

  1. Результаты этого конструктора могут быть несколько непредсказуемыми. Можно предположить, что при записи new BigDecimal(0.1) на Java создается BigDecimal значение, которое в точности равно 0,1 (немасштабированное значение 1 со шкалой 1), но на самом деле оно равно 0,100000000000000000055511151231257827021181583404541015625. Это потому, что 0.1 не может быть представлено точно как double (или, если уж на то пошло, как двоичная дробь любой конечной длины). Таким образом, значение, которое передается в конструктор, не совсем равно 0.1, несмотря на внешний вид.

  2. String Конструктор, с другой стороны, совершенно предсказуем: при записи new BigDecimal("0.1") создается BigDecimal значение, в точности равное 0.1, как и следовало ожидать. Поэтому обычно рекомендуется использовать String конструктор предпочтительнее этого.

  3. Когда a double необходимо использовать в качестве источника для a BigDecimal , обратите внимание, что этот конструктор обеспечивает точное преобразование; он не дает того же результата, что преобразование double в a String с помощью Double.toString(double) метода, а затем с помощью BigDecimal(String) конструктора. Чтобы получить этот результат, используйте static valueOf(double) метод.

Итак, подведем итог: если вы хотите создать BigDecimal с фиксированным десятичным значением, используйте String конструктор. Если у вас уже есть double значение, то BigDecimal.valueOf(double) это обеспечит более интуитивное поведение, чем использование new BigDecimal(double) .