BigDecimal сравнить, чтобы не работать как положено - PullRequest
13 голосов
/ 19 мая 2011

Согласно 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 Ответ

31 голосов
/ 19 мая 2011

new BigDecimal(0.7) не не представляет 0,7.

Это 0,6999999999999999555910790149937383830547332763671875 (точно).

Причина этого в том, что 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,1000000000000000055511151231257827021181583404541015625. Это потому, что 0,1 не может быть представлен точно как double (или, в этом отношении, как двоичная дробь любой конечной длины). Таким образом, значение, которое передается в конструктор, не совсем равно 0,1, несмотря на внешний вид.

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

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

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

...