Почему 24.0000 не равно 24.0000 в MATLAB?
-
22-08-2019 - |
Вопрос
Я пишу программу, в которой мне нужно удалить повторяющиеся точки, хранящиеся в матрице.Проблема в том, что когда дело доходит до проверки того, есть ли эти точки в матрице, 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
Решение
Проблема, с которой вы столкнулись, связана с тем, как числа с плавающей запятой представлены на компьютере.Более подробное обсуждение представлений с плавающей запятой появляется ближе к концу моего ответа (раздел "Представление с плавающей запятой").В 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 битами , расположенными в памяти следующим образом:
Затем реальное значение можно найти с помощью следующей формулы:
Этот формат допускает представление чисел в диапазоне от 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.
Предупреждение:Вы можете быть удивлены результатом!
Возможно, эти два числа на самом деле 24.0 и 24.000000001, но вы видите не все десятичные разряды.
Ознакомьтесь с Функция Matlab EPS.
Matlab использует математику с плавающей запятой с точностью до 16 цифр (отображается только 5).