#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)
конструктора есть некоторые связанные примечания:
Результаты этого конструктора могут быть несколько непредсказуемыми. Можно предположить, что при записи
new BigDecimal(0.1)
на Java создаетсяBigDecimal
значение, которое в точности равно 0,1 (немасштабированное значение 1 со шкалой 1), но на самом деле оно равно 0,100000000000000000055511151231257827021181583404541015625. Это потому, что 0.1 не может быть представлено точно какdouble
(или, если уж на то пошло, как двоичная дробь любой конечной длины). Таким образом, значение, которое передается в конструктор, не совсем равно 0.1, несмотря на внешний вид.
String
Конструктор, с другой стороны, совершенно предсказуем: при записиnew BigDecimal("0.1")
создаетсяBigDecimal
значение, в точности равное 0.1, как и следовало ожидать. Поэтому обычно рекомендуется использоватьString
конструктор предпочтительнее этого.Когда a
double
необходимо использовать в качестве источника для aBigDecimal
, обратите внимание, что этот конструктор обеспечивает точное преобразование; он не дает того же результата, что преобразованиеdouble
в aString
с помощьюDouble.toString(double)
метода, а затем с помощьюBigDecimal(String)
конструктора. Чтобы получить этот результат, используйтеstatic
valueOf(double)
метод.
Итак, подведем итог: если вы хотите создать BigDecimal
с фиксированным десятичным значением, используйте String
конструктор. Если у вас уже есть double
значение, то BigDecimal.valueOf(double)
это обеспечит более интуитивное поведение, чем использование new BigDecimal(double)
.