BigDecimal compareНе работает как ожидалось

В соответствии с 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

new BigDecimal(0.7) имеет не 0,7.

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

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

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

Попробуйте new BigDecimal("0.7").

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

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

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

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

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