Почему 24.0000 не равно 24.0000 в MATLAB?
Я пишу программу, в которой мне нужно удалить дубликаты точек, хранящихся в матрице. Проблема в том, что когда дело доходит до проверки, находятся ли эти точки в матрице, 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 ответов
проблема вас касается как числа с плавающей точкой представлены на компьютере. Более подробное обсуждение представлений с плавающей запятой появляется в конце моего ответа (раздел" представление с плавающей запятой"). The TL; DR версия: поскольку компьютеры имеют конечные объемы памяти, числа могут быть представлены только с конечной точностью. Таким образом, точность чисел с плавающей запятой ограничена определенным числом десятичных знаков места (около 16 значащих цифр для двойной точности значения по умолчанию используется в MATLAB).
фактическая и отображаемая точность
Теперь рассмотрим конкретный пример в вопросе... пока 24.0000
и 24.0000
are отображается таким же образом, оказывается, что они на самом деле отличаются очень маленькими десятичными суммами в этом случае. Вы не видите его, потому что MATLAB отображает только 4 значащие цифры по умолчанию, сохраняя общий дисплей аккуратным и аккуратным. если вы хотите увидеть полную точность, вы должны либо выдать просмотр шестнадцатеричное представление номер:
>> 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 для арифметики с плавающей запятой) is Что Каждый Компьютерщик Должен Знать Об Арифметике С Плавающей Запятой Дэвид Голдберг.
двоичное число с плавающей запятой представляется тремя целыми числами: бит знака 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
Проверьте функция EPS Matlab.
Matlab использует математику с плавающей запятой до 16 цифр точности (отображаются только 5).