Ошибки округления?

В моем курсе мне сказали:

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

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

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

Ответ 1

Это правда.

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

Эта программа, например, печатает "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);

Ответ 2

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

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

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

Ответ 3

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

Ответ 4

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

Ответ 5

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

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

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

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

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

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

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

Ответ 6

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

Ответ 8

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

Изображение: "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?

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

Ответ 9

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

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

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

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