Ошибки округления?
В моем курсе мне сказали:
Непрерывные значения представлены примерно в памяти, поэтому вычисление с помощью 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
Да, как говорили другие ответы. Я хочу добавить, что я рекомендую вам эту статью о точности с плавающей запятой: Визуализация поплавков
Ответ 7
да, Java также использует арифметику с плавающей запятой.
Ответ 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, а также может сравниваться безопасно. Например, их можно безопасно использовать в качестве счетчиков циклов.