«новый 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(), и я не могу это изменить :-(

Ваше здоровье.

На самом деле версия toString () - это, вероятно, то, что вам нужно. Десятичные числа не могут быть правильно представлены как двойные, и хотя BigDecimals решают эту проблему, предоставление им двойного числа в конструкторе подрывает решение, поскольку у них нет точного числа, с которого можно было бы начать.

Andrzej Doyle 20.01.2009 14:04
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
16
1
4 679
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Это не вина BigDecimal - это вина double. BigDecimal точно представляет значение точныйd. String.valueOf показывает результат только с точностью до нескольких десятичных знаков.

Возможно, вы захотите узнать, как реализованы значения с плавающей запятой (IEEE 754-1985). И вдруг все станет предельно ясно.

Ваша проблема не имеет ничего общего с BigDecimal, и все с Double, который не может точно представлять 13,3, так как он внутренне использует двоичные дроби.

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

Ответ принят как подходящий

Что ж, API действительно устраняет эту очевидную несогласованность в конструкторе BigDecimal(double val):

  1. The results of this constructor can be somewhat unpredictable. One might assume that writing new BigDecimal(0.1) in Java creates a BigDecimal which is exactly equal to 0.1 (an unscaled value of 1, with a scale of 1), but it is actually equal to 0.1000000000000000055511151231257827021181583404541015625. This is because 0.1 cannot be represented exactly as a double (or, for that matter, as a binary fraction of any finite length). Thus, the value that is being passed in to the constructor is not exactly equal to 0.1, appearances notwithstanding.

  2. The String constructor, on the other hand, is perfectly predictable: writing new BigDecimal("0.1") creates a BigDecimal which is exactly equal to 0.1, as one would expect. Therefore, it is generally recommended that the String constructor be used in preference to this one.

  3. When a double must be used as a source for a BigDecimal, note that this constructor provides an exact conversion; it does not give the same result as converting the double to a String using the Double.toString(double) method and then using the BigDecimal(String) constructor. To get that result, use the static valueOf(double) method.

Мораль истории: боль кажется причиненной самому себе, просто используйте вместо нее new BigDecimal(String val) или BigDecimal.valueOf(double val) =)

+1: Вот как действительно работает Double. Ничего общего с BigDecimal.

S.Lott 20.01.2009 14:03

Хороший. Так что действительно кто-то должен бросить мне RTFM ;-)

Damo 20.01.2009 14:43

Обратите внимание, что новый BigDecimal (d + "") также работает нормально. Результат в «13.3» такой же, как и при использовании valueOf (d)

BAERUS 28.05.2015 15:27

Дроби, представленные типами двоичных чисел (например, 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).

Другие вопросы по теме