"новый BigDecimal (13.3D)" приводит к неточности "13.3000000000000007105.."?
Как это может выглядеть так: Java BigDecimal
?
Double d = 13.3D;
BigDecimal bd1 = new BigDecimal(d);
BigDecimal bd2 = new BigDecimal(String.valueOf(d));
System.out.println("RESULT 1: "+bd1.toString());
System.out.println("RESULT 2: "+bd2.toString());
RESULT 1: 13.300000000000000710542735760100185871124267578125
RESULT 2: 13.3
Есть ли какая-либо ситуация, когда требуется Результат 1? Я знаю, что Java 1.5 изменил метод toString()
, но был ли это предполагаемым последствием?
Также я понимаю, что BigDecimal
имеет doubleValue()
и т.д., но в библиотеке, с которой я работаю, полезно использовать toString()
, и я не могу изменить это: - (
Приветствия.
Ответы
Ответ 1
Ну, API устраняет эту кажущуюся несогласованность в конструкторе BigDecimal(double val)
:
-
Результаты этого конструктора могут быть несколько непредсказуемыми. Можно было бы предположим, что BigDecimal (0.1) в Java создает BigDecimal, который в точности равен 0,1 (немасштабированное значение 1, с шкалой 1), но оно фактически равно в 0,1000000000000000055511151231257827021181583404541015625. Это потому, что 0,1 не может быть представленный точно как двойной (или, в этом отношении, как двоичная фракция любой конечной длины). Таким образом, значение который передается в конструктор не точно равен 0,1, несмотря на это.
-
Конструктор String, с другой стороны, вполне предсказуем: запись нового BigDecimal ( "0.1" ) создает BigDecimal, который в точности равен 0,1, как и следовало ожидать. Поэтому рекомендуется, чтобы Строковый конструктор можно использовать в предпочтение этому.
-
Когда двойной должен использоваться как источник для BigDecimal, обратите внимание, что этот конструктор обеспечивает точное преобразование; он не дает того же результат как преобразование двойного в Строка с использованием Метод Double.toString(double) и затем используя BigDecimal (String) конструктор. Чтобы получить этот результат, используйте статический метод valueOf (double).
Мораль истории: боль кажется самонадеянной, просто используйте new BigDecimal(String val)
или BigDecimal.valueOf(double val)
вместо =)
Ответ 2
Ваша проблема не имеет ничего общего с BigDecimal
и все с Double
, которая не может точно представлять 13.3, так как она использует двоичные дроби внутри.
Итак, ваша ошибка введена в первой строке. Первый BigDecimal
просто сохраняет его, а String.valueOf()
делает какое-то подозрительное округление, которое заставляет второй иметь желаемый контент, в значительной степени благодаря удаче.
Ответ 3
Возможно, вам захочется сообщить о том, как реализованы значения с плавающей запятой (IEEE 754-1985). И вдруг все станет кристально чистым.
Ответ 4
Это не ошибка BigDecimal
- это ошибка double
. BigDecimal
точно представляет точное значение d
. String.valueOf
показывает результат только на несколько десятичных знаков.
Ответ 5
Фракции, представленные двоичными типами (т.е. 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 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)
.