Почему 24,0000 не равно 24,0000 в MATLAB? - PullRequest
63 голосов
/ 26 марта 2009

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

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


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

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

Ответы [ 6 ]

90 голосов
/ 26 марта 2009

Ваша проблема связана с тем, как числа с плавающей точкой представлены на компьютере. Более подробное обсуждение представлений с плавающей точкой появляется в конце моего ответа (раздел «Представление с плавающей точкой»). Версия 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 не имеет точного представления с плавающей запятой (то есть оно слегка округляется), и поэтому в результате вы получите противоположные результаты ошибки накапливаются:

>> 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's 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 битами, расположенными в памяти следующим образом:

enter image description here

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

enter image description here

Этот формат допускает представление чисел в диапазоне от 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 значимых цифры. Это иллюстрирует, как смещение или масштабирование данных могут изменить точность вычислений, выполненных с ними, что необходимо учитывать при определенных проблемах.

22 голосов
/ 26 марта 2009

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

13 голосов
/ 27 марта 2009

Тип

format long g

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

7 голосов
/ 14 декабря 2011

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

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

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

2 голосов
/ 26 марта 2009

Возможно, эти два числа действительно 24,0 и 24,000000001, но вы не видите все десятичные знаки.

1 голос
/ 26 марта 2009

Проверьте функцию Matlab EPS .

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

...