Ошибки округления? - PullRequest
21 голосов
/ 06 июня 2009

В моем курсе мне говорят:

Непрерывные значения представлены приблизительно в памяти, и, следовательно, вычисления с плавающей запятой связаны с ошибками округления. Это крошечные расхождения в битовых комбинациях; таким образом, тест e==f небезопасен, если e и f являются числами с плавающей запятой.

Ссылаясь на Java.

Это правда? Я использовал операторы сравнения с double s и float s и никогда не сталкивался с проблемами округления. Никогда я не читал в учебнике нечто подобное. Конечно, виртуальная машина отвечает за это?

Ответы [ 9 ]

50 голосов
/ 06 июня 2009

Это правда.

Это внутреннее ограничение того, как значения с плавающей запятой представляются в памяти конечным числом битов.

Эта программа, например, выводит «false»:

public class Main {
  public static void main(String[] args) {
    double a = 0.7;
    double b = 0.9;
    double x = a + 0.1;
    double y = b - 0.1;
    System.out.println(x == y);
  }
}

Вместо точного сравнения с '==' вы обычно выбираете какой-то уровень точности и спрашиваете, достаточно ли близки числа:

System.out.println(Math.abs(x - y) < 0.0001);
25 голосов
/ 06 июня 2009

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

Дополнительная информация о значениях с плавающей запятой:

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

8 голосов
/ 06 июня 2009

Да, представление 0.1 точно в базе-2 - это то же самое, что пытаться представить 1/3 точно в базе 10.

4 голосов
/ 06 июня 2009

Это правильно. Обратите внимание, что Java не имеет к этому никакого отношения, проблема присуща математике с плавающей запятой в ANY языке.

Вы часто можете сойти с рук с проблемами на уровне класса, но это не сработает в реальном мире. Иногда это не сработает в классе.

Инцидент с давних времен в школе. Преподаватель вступительного класса поставил задачу по выпускному экзамену, которая оказалась для многих из лучших учеников настоящим испытанием - это не сработало, и они не знали, почему. (Я видел это как лаборанта, меня не было в классе.) Наконец некоторые начали просить меня о помощи, и некоторые исследования выявили проблему: их никогда не учили о врожденной неточности математики с плавающей запятой.

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

Конечно, в следующем семестре я слышу от него об этом, и я в основном заполнил весь отдел простой маленькой программой:

10 X = 3000000
20 X = X + 1
30 If X < X + 1 goto 20
40 Print "X = X + 1"

Несмотря на то, что думал каждый преподаватель кафедры, это БУДЕТ прекратить. 3 миллиона семян просто для того, чтобы они заканчивались быстрее. (Если вы не знаете основы: здесь нет никаких уловок, просто исчерпывается точность чисел с плавающей запятой.)

4 голосов
/ 06 июня 2009

Это всегда так. Есть некоторые числа, которые не могут быть точно представлены с использованием представления с плавающей точкой. Рассмотрим, например, пи. Как бы вы представили число с бесконечными цифрами в пределах конечного хранилища? Поэтому при сравнении чисел вы должны проверить, меньше ли разница между ними, чем некоторые эпсилоны. Также существует несколько классов, которые могут помочь вам достичь большей точности, таких как BigDecimal и BigInteger.

2 голосов
/ 06 июня 2009

Большинство процессоров (и компьютерных языков) используют арифметику IEEE 754 с плавающей запятой. Используя эту запись, существуют десятичные числа, которые не имеют точного представления в этой записи, например 0,1. Таким образом, если вы разделите 1 на 10, вы не получите точного результата. При выполнении нескольких расчетов подряд ошибки суммируются. Попробуйте следующий пример в Python:

>>> 0.1
0.10000000000000001
>>> 0.1 / 7 * 10 * 7 == 1
False

Это не совсем то, что вы ожидаете математически.

Кстати: Распространенным заблуждением относительно чисел с плавающей запятой является то, что результаты не являются точными и не могут быть сопоставлены безопасно. Это верно только в том случае, если вы действительно используете дроби чисел. Если вся ваша математика находится в целочисленной области, удваивания и числа с плавающей точкой делают точно так же, как и целые числа, и их также можно безопасно сравнивать. Например, их можно безопасно использовать в качестве счетчиков циклов.

2 голосов
/ 06 июня 2009

Да, как сказали другие ответы. Я хочу добавить, что я рекомендую вам эту статью о точности с плавающей запятой: Визуализация поплавков

1 голос
/ 06 июня 2009

Конечно, это правда. Думаю об этом. Любое число должно быть представлено в двоичном виде.

Изображение: «1000» как 0,5 или 1/2, то есть 2 ** -1. Тогда «0100» равно 0,25 или 1/4. Вы можете видеть, куда я иду.

Сколько чисел вы можете представить таким образом? 2 ** 4. Добавление большего количества бит дублирует доступное пространство, но оно никогда не бывает бесконечным. 1/3 или 1/10, для вопроса 1 / n, любое число, не кратное 2, не может быть действительно представлено.

1/3 может быть «0101» (0,3125) или «0110» (0,375). Любое значение, если вы умножите его на 3, не будет равно 1. Конечно, вы можете добавить специальные правила. Скажем «когда вы добавляете 3 раза« 0101 », сделайте это 1» ... этот подход не будет работать в долгосрочной перспективе. Вы можете поймать немного, но тогда как насчет 1/6 раза 2?

Это не проблема двоичного представления, у любого конечного представления есть числа, которые вы не можете представить, ведь они бесконечны.

1 голос
/ 06 июня 2009

да, Java также использует арифметику с плавающей точкой .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...