Я пишу программу, где мне нужно удалить повторяющиеся точки, хранящиеся в матрице. Проблема в том, что, когда дело доходит до проверки того, находятся ли эти точки в матрице, 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
Проблема, с которой вы связаны, связана с тем, как числа с плавающей запятой представлены на компьютере. Более подробное обсуждение представлений с плавающей запятой появляется к концу моего ответа (раздел "Плавающее представление" ). Версия 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 битами, выложенными в памяти следующим образом:
Реальное значение можно найти по следующей формуле:
Этот формат допускает числовые представления в диапазоне от 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 существенных цифры. Это иллюстрирует, как смещение или масштабирование данных может изменить точность выполненных на нем вычислений, что должно быть связано с определенными проблемами.
Посмотрите на эту статью: Опасности плавающей точки. Хотя его примеры в FORTRAN имеют смысл практически для любого современного языка программирования, включая MATLAB. Ваша проблема (и решение для нее) описана в разделе "Безопасные сравнения".
тип
format long g
Эта команда покажет ПОЛНОЕ значение числа. Вероятно, это будет что-то вроде 24.00000021321!= 24.00000123124
Попробуйте написать
0,1 + 0,1 + 0,1 == 0,3.
Предупреждение: вы можете быть удивлены результатом!
Просмотрите функцию EPS Matlab.
Matlab использует математику с плавающей запятой до 16 цифр точности (отображается только 5).
Возможно, эти два номера действительно 24.0 и 24.000000001, но вы не видите все десятичные знаки.