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