Вопрос

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

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

Ссылаясь на Яву.

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

Это было полезно?

Решение

Это правда.

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

Эта программа, например, выводит «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);

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

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

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

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

Да, представление 0,1 точно по основанию 2 — это то же самое, что попытка представить 1/3 точно по основанию 10.

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

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

Часто вам это сходит с рук при решении проблем на уровне класса, но в реальном мире это не сработает.Иногда это не работает в классе.

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

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

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

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

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

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

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

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

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

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

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

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

Картина:«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?

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top