Почему 24,0000 не равно 24,0000 в MATLAB?

56

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

В следующем коде функция intersections получает точки пересечения:

[points(:,1), points(:,2)] = intersections(...
    obj.modifiedVGVertices(1,:), obj.modifiedVGVertices(2,:), ...
    [vertex1(1) vertex2(1)], [vertex1(2) vertex2(2)]);

Результат:

>> points
points =
   12.0000   15.0000
   33.0000   24.0000
   33.0000   24.0000

>> vertex1
vertex1 =
    12
    15

>> vertex2    
vertex2 =
    33
    24

Две точки (vertex1 и vertex2) должны быть исключены из результата. Это должно быть сделано с помощью следующих команд:

points = points((points(:,1) ~= vertex1(1)) | (points(:,2) ~= vertex1(2)), :);
points = points((points(:,1) ~= vertex2(1)) | (points(:,2) ~= vertex2(2)), :);

После этого мы получаем этот неожиданный результат:

>> points
points =
   33.0000   24.0000

Результат должен быть пустой матрицей. Как вы можете видеть, первая (или вторая?) Пара [33.0000 24.0000] была устранена, но не вторая.

Затем я проверил эти два выражения:

>> points(1) ~= vertex2(1)
ans =
     0
>> points(2) ~= vertex2(2)
ans =
     1   % <-- It means 24.0000 is not equal to 24.0000?

В чем проблема?


Более удивительно, что я создал новый script, который имеет только следующие команды:

points = [12.0000   15.0000
          33.0000   24.0000
          33.0000   24.0000];

vertex1 = [12 ;  15];
vertex2 = [33 ;  24];

points = points((points(:,1) ~= vertex1(1)) | (points(:,2) ~= vertex1(2)), :);
points = points((points(:,1) ~= vertex2(1)) | (points(:,2) ~= vertex2(2)), :);

Результат, как ожидалось:

>> points
points =  
   Empty matrix: 0-by-2
  • 1
    Это также было решено здесь
  • 2
    @Kamran: Извините, я не указал на опасности сравнения с плавающей запятой, когда вы спросили о сравнении значений в другом вопросе. Мне не сразу пришло в голову, что вы можете столкнуться с этой проблемой.
Показать ещё 13 комментариев
Теги:
floating-point
precision

6 ответов

80
Лучший ответ

Проблема, с которой вы связаны, связана с тем, как числа с плавающей запятой представлены на компьютере. Более подробное обсуждение представлений с плавающей запятой появляется к концу моего ответа (раздел "Плавающее представление" ). Версия TL; DR: поскольку компьютеры имеют ограниченное количество памяти, цифры могут быть представлены только с конечной точностью. Таким образом, точность чисел с плавающей запятой ограничена определенным количеством десятичных знаков (около 16 значащих цифр для значений двойной точности, значение по умолчанию используется в MATLAB).

Фактическая и отображаемая точность

Теперь, чтобы обратиться к конкретному примеру в вопросе... пока 24.0000 и 24.0000 отображаются таким же образом, оказывается, что в этом случае они фактически отличаются очень маленькими десятичными суммами. Вы не видите этого, потому что MATLAB отображает только 4 значащих цифры по умолчанию, сохраняя общий дисплей опрятным и аккуратным. Если вы хотите чтобы увидеть полную точность, вы должны либо выдать команду format long, либо просмотреть шестнадцатеричное представление номера:

>> pi
ans =
    3.1416
>> format long
>> pi
ans =
   3.141592653589793
>> num2hex(pi)
ans =
400921fb54442d18

Инициализированные значения против вычисленных значений

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

>> a=sum([0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1]);  % Sum 10 0.1s
>> b=1;                                               % Initialize to 1
>> a == b
ans =
  logical
   0                % They are unequal!
>> num2hex(a)       % Let check their hex representation to confirm
ans =
3fefffffffffffff
>> num2hex(b)
ans =
3ff0000000000000

Как правильно обрабатывать сравнения с плавающей запятой

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

a = 24;
b = 24.000001;
tolerance = 0.001;
if abs(a-b) < tolerance, disp('Equal!'); end

отобразит "Равно!".

Затем вы можете изменить свой код на что-то вроде:

points = points((abs(points(:,1)-vertex1(1)) > tolerance) | ...
                (abs(points(:,2)-vertex1(2)) > tolerance),:)

Представление с плавающей запятой

Хороший обзор чисел с плавающей запятой (и, в частности, стандарт IEEE 754 для арифметики с плавающей запятой) Что каждый компьютерный ученый должен знать о арифметике с плавающей точкой Дэвида Голдберга.

Двоичное число с плавающей запятой фактически представлено тремя целыми числами: знаковый бит s, значение (или коэффициент/доля) b и показатель e. Для формата с плавающей запятой с двойной точностью каждый номер представлен 64 битами, выложенными в памяти следующим образом:

Изображение 6294

Реальное значение можно найти по следующей формуле:

Изображение 6295

Этот формат допускает числовые представления в диапазоне от 10 ^ -308 до 10 ^ 308. Для MATLAB вы можете получить эти ограничения из realmin и realmax:

>> realmin
ans =
    2.225073858507201e-308
>> realmax
ans =
    1.797693134862316e+308

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

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

>> eps(1)
ans =
     2.220446049250313e-16
>> eps(1000)
ans =
     1.136868377216160e-13

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

>> format long              % Display full precision
>> x = rand(1, 10);         % Get 10 random values between 0 and 1
>> a = mean(x)              % Take the mean
a =
   0.587307428244141
>> b = mean(x+10000)-10000  % Take the mean at a different scale, then shift back
b =
   0.587307428244458

Обратите внимание, что когда мы смещаем значения x из диапазона [0 1] в диапазон [10000 10001], вычисляем среднее значение, а затем вычитаем среднее смещение для сравнения, получаем значение, которое отличается для последних 3 существенных цифры. Это иллюстрирует, как смещение или масштабирование данных может изменить точность выполненных на нем вычислений, что должно быть связано с определенными проблемами.

  • 0
    почему я не вижу эту маленькую десятичную сумму?
  • 2
    Вы можете увидеть это, если посмотрите на переменную в матричном представлении. Щелкните правой кнопкой мыши по переменной -> «Просмотреть выделение» или что-то? У меня нет MATLAB здесь, поэтому я не могу проверить.
Показать ещё 5 комментариев
19

Посмотрите на эту статью: Опасности плавающей точки. Хотя его примеры в FORTRAN имеют смысл практически для любого современного языка программирования, включая MATLAB. Ваша проблема (и решение для нее) описана в разделе "Безопасные сравнения".

  • 1
    Я обнаружил это некоторое время назад и был очень впечатлен этим =) Теперь я всегда рекомендую это в подобных ситуациях.
  • 0
    Архивная версия этого отличного ресурса!
12

тип

format long g

Эта команда покажет ПОЛНОЕ значение числа. Вероятно, это будет что-то вроде 24.00000021321!= 24.00000123124

7

Попробуйте написать

0,1 + 0,1 + 0,1 == 0,3.

Предупреждение: вы можете быть удивлены результатом!

  • 0
    Я попробовал, и он возвращает 0. Но я не вижу, что он должен делать, с проблемой выше. Можете ли вы объяснить это мне?
  • 6
    Это происходит потому, что 0.1 имеет некоторую ошибку с плавающей запятой, и когда вы добавляете три таких термина вместе, ошибки не обязательно добавляют до 0. Та же проблема приводит к тому, что (плавающее) 24 не совсем точно равно (другое плавающее) 24 ,
1

Просмотрите функцию EPS Matlab.

Matlab использует математику с плавающей запятой до 16 цифр точности (отображается только 5).

1

Возможно, эти два номера действительно 24.0 и 24.000000001, но вы не видите все десятичные знаки.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню