Ошибка точности с плавающей точкой в ​​Java - PullRequest
10 голосов
/ 16 июля 2011

Мне интересно, как лучше всего исправить ошибки точности в Java.Как вы можете видеть в следующем примере, есть ошибки точности:

class FloatTest
{
  public static void main(String[] args)
  {
    Float number1 = 1.89f;

    for(int i = 11; i < 800; i*=2)
    {
      System.out.println("loop value: " + i);
      System.out.println(i*number1);
      System.out.println("");
    }
  }
}

Отображаемый результат:

значение цикла: 11

20.789999

значение цикла: 22 * ​​1011 *

41.579998

значение цикла: 44

83.159996

значение цикла: 88

166.31999

значение цикла: 176

332.63998

значение цикла: 352

665.27997

значение цикла: 704

1330.5599

Кроме того, если кто-то может объяснить, почему он делает это, начиная с 11 и удваивая значение каждый раз.Я думаю, что все другие значения (или, по крайней мере, многие из них) отображали правильный результат.

Подобные проблемы вызывали у меня головную боль в прошлом, и я обычно использую средства форматирования чисел или помещаю их в строку.

Редактировать: Как уже упоминалось, я мог бы использовать удвоение, но после попытки, кажется, что 1,89 как удвоенное число 792 по-прежнему выдает ошибку (вывод 1496.8799999999999).

Думаю, я попробую другие решения, такие как BigDecimal

Ответы [ 8 ]

8 голосов
/ 16 июля 2011

Если вы действительно заботитесь о точности, вы должны использовать BigDecimal

http://download.oracle.com/javase/1,5.0/docs/api/java/math/BigDecimal.html

7 голосов
/ 16 июля 2011

Проблема не в Java, а в хорошем стандартном float (http://en.wikipedia.org/wiki/IEEE_floating-point_standard).

. Вы можете:

  • использовать Double и иметь немного большую точность (но не идеальный, конечно, он также имеет ограниченную точность)

  • использовать библиотеку произвольной точности

  • использовать численно устойчивые алгоритмы и усекать /круглые цифры, в которых вы не уверены, что они верны (вы можете вычислить числовую точность операций)

4 голосов
/ 16 июля 2011

Когда вы печатаете результат двойной операции, вам нужно использовать соответствующее округление.

System.out.printf("%.2f%n", 1.89 * 792);

печать

1496.88

Если вы хотите округлить результат с точностью, вы можете использовать округление.

double d = 1.89 * 792;
d = Math.round(d * 100) / 100.0;
System.out.println(d);

печать

1496.88

Однако, если вы видите ниже, он печатается, как и ожидалось, поскольку подразумевается небольшое количество подразумеваемых округлений.


Ничего не стоит, что (double) 1.89 не совсем 1.89 Это близкое приближение.

new BigDecimal (double) преобразует точное значение double без какого-либо подразумеваемого округления. Это может быть полезно при нахождении точного значения типа double.

System.out.println(new BigDecimal(1.89));
System.out.println(new BigDecimal(1496.88));

печать

1.8899999999999999023003738329862244427204132080078125
1496.8800000000001091393642127513885498046875
3 голосов
/ 10 ноября 2011

Большая часть вашего вопроса была довольно хорошо рассмотрена, хотя вы все равно можете почитать [floating-point] тэг вики , чтобы понять , почему другие ответы работают.

Однако никто не обратился к вопросу «почему он делает это, начиная с 11 и удваивая значение каждый раз», поэтому вот ответ на этот вопрос:

for(int i = 11; i < 800; i*=2)
    ╚═══╤════╝           ╚╤═╝
        │                 └───── "double the value every time"
        │
        └───── "start at 11"
2 голосов
/ 16 июля 2011

Если вам действительно нужна произвольная точность, используйте BigDecimal .

2 голосов
/ 16 июля 2011

Вы можете использовать двойные числа вместо числа с плавающей точкой

0 голосов
/ 16 июля 2011

Если точность важна, вы должны использовать BigDecimal, чтобы убедиться, что требуемая точность сохраняется.Когда вы создаете экземпляр вычисления, не забывайте использовать строки для создания значений вместо double.

0 голосов
/ 16 июля 2011

first of Float - это класс-оболочка для примитива float

, а удвоения имеют большую точность

, но если вы хотите рассчитать только до второй цифры (для денежногонапример, используйте целое число (как если бы вы использовали центы как единицу) и добавьте некоторую логику масштабирования, когда вы умножаете / делите

или если вам нужна произвольная точность, используйте BigDecimal

...