Ну, API действительно обращается к этому очевидному несоответствию в конструкторе BigDecimal(double val)
:
результаты этого конструктора могут быть несколько непредсказуемыми. Можно было бы предположить, что запись нового BigDecimal (0.1) в Java создает BigDecimal, который точно равен 0,1 (немасштабированное значение 1, с масштабом 1), но это на самом деле равно 0,1000000000000000055511151231257827021181583404541015625. Это вызвано тем, что 0.1 не может быть представлен точно как двойное (или, в этом отношении, как двоичная дробь никакой конечной длины). Таким образом значение, которое передается в конструктору, не точно равно 0,1, появления, несмотря на это.
Строковый конструктор, с другой стороны, совершенно предсказуем: запись нового BigDecimal ("0.1") создает BigDecimal, который точно равен 0,1, как можно было бы ожидать. Поэтому обычно рекомендуется, чтобы конструктор String использовался в предпочтении к этому.
, Когда двойное должно использоваться в качестве источника для BigDecimal, обратите внимание, что этот конструктор обеспечивает точное преобразование; это не дает тот же результат как преобразование вдвое большего по сравнению со Строкой с помощью Double.toString (двойной) метод и затем с помощью BigDecimal (Строка) конструктор. Для получения того результата, используют статический valueOf (двойной) метод .
Мораль истории: боль кажется нанесенной самому себе, просто используйте new BigDecimal(String val)
или BigDecimal.valueOf(double val)
вместо этого =)
Ваша проблема не имеет никакого отношения BigDecimal
, и все с Double
, который не может представить 13.3 точно, так как она использует двоичные дроби внутренне.
, Таким образом, Ваша ошибка представлена в самой первой строке. Первое BigDecimal
просто консервы это, в то время как String.valueOf()
делает некоторое подозрительное округление, которое заставляет второй иметь желаемое содержание, в значительной степени через удачу.
Вы могли бы хотеть сообщить себе о том, как значения с плавающей точкой реализованы ( IEEE 754-1985 ). И внезапно, все станет совершенно ясным.
Это не отказ BigDecimal
- это - отказ double
. BigDecimal
точно представляет точный значение d
. String.valueOf
только показывает результат нескольким десятичным разрядам.
Части, представленные с типами двоичного числа (т.е. double
, float
), не могут быть точно сохранены в тех типах.
Double d = 13.3;
BigDecimal bdNotOk = new BigDecimal(d);
System.out.println("not ok: " + bdNotOk.toString());
BigDecimal bdNotOk2 = new BigDecimal(13.3);
System.out.println("not ok2: " + bdNotOk2.toString());
double x = 13.3;
BigDecimal ok = BigDecimal.valueOf(x);
System.out.println("ok: " + ok.toString());
double y = 13.3;
// pretty lame, constructor's behavior is different from valueOf static method
BigDecimal bdNotOk3 = new BigDecimal(y);
System.out.println("not ok3: " + bdNotOk3.toString());
BigDecimal ok2 = new BigDecimal("13.3");
System.out.println("ok2: " + ok2.toString());
Double e = 0.0;
for(int i = 0; i < 10; ++i) e = e + 0.1; // some fractions cannot be accurately represented with binary
System.out.println("not ok4: " + e.toString()); // should be 1
BigDecimal notOk5 = BigDecimal.valueOf(e);
System.out.println("not ok5: " + notOk5.toString()); // should be 1
/*
* here are some fractions that can be represented exactly in binary:
* 0.5 = 0.1 = 1 / 2
* 0.25 = 0.01 = 1 / 4
* 0.75 = 0.11 = 3 / 4
* 0.125 = 0.001 = 1 / 8
*/
вывод:
not ok: 13.300000000000000710542735760100185871124267578125
not ok2: 13.300000000000000710542735760100185871124267578125
ok: 13.3
not ok3: 13.300000000000000710542735760100185871124267578125
ok2: 13.3
not ok4: 0.9999999999999999
not ok5: 0.9999999999999999
Просто использование BigDecimal.valueOf(d)
или new BigDecimal(s)
.