69

我正在编写一个程序,我需要删除存储在矩阵中的重复点。问题是,当检查这些点是否在矩阵中时,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
4

6 回答 6

98

您遇到的问题与浮点数在计算机上的表示方式有关。浮点表示的更详细讨论出现在我的答案的末尾(“浮点表示”部分)。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 浮点算术标准)的一个很好的概述是David Goldberg的《每一个计算机科学家都应该知道的浮点算术》。

二进制浮点数实际上由三个整数表示:符号位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从 range[0 1]移到 range[10000 10001]时,计算一个平均值,然后减去平均偏移量以进行比较,我们得到一个与最后 3 位有效数字不同的值。这说明了数据的偏移或缩放如何改变对其执行的计算的准确性,这是某些问题必须考虑的问题。

于 2009-03-26T16:14:52.020 回答
23

看这篇文章:浮点的危险。尽管它的示例在 FORTRAN 中,但它几乎适用于任何现代编程语言,包括 MATLAB。您的问题(及其解决方案)在“安全比较”部分中进行了描述。

于 2009-03-26T16:18:11.623 回答
13

类型

format long g

此命令将显示数字的完整值。它可能类似于 24.00000021321 != 24.00000123124

于 2009-03-26T21:14:29.210 回答
7

尝试写作

0.1 + 0.1 + 0.1 == 0.3。

警告:您可能会对结果感到惊讶!

于 2011-12-14T18:55:46.443 回答
2

也许这两个数字实际上是 24.0 和 24.000000001,但您没有看到所有小数位。

于 2009-03-26T16:17:23.760 回答
1

查看Matlab EPS 函数

Matlab 使用高达 16 位精度的浮点数学运算(仅显示 5 位)。

于 2009-03-26T16:25:45.287 回答