为什么在MATLAB中24.0000不等于24.0000?

为什么在MATLAB中24.0000不等于24.0000?,matlab,floating-point,precision,Matlab,Floating Point,Precision,我正在编写一个程序,需要删除矩阵中存储的重复点。问题是,当检查这些点是否在矩阵中时,MATLAB无法识别矩阵中的这些点,尽管它们存在 在以下代码中,交叉点函数获取交叉点: [points(:,1), points(:,2)] = intersections(... obj.modifiedVGVertices(1,:), obj.modifiedVGVertices(2,:), ... [vertex1(1) vertex2(1)], [vertex1(2) vertex2(2)

我正在编写一个程序,需要删除矩阵中存储的重复点。问题是,当检查这些点是否在矩阵中时,MATLAB无法识别矩阵中的这些点,尽管它们存在

在以下代码中,
交叉点
函数获取交叉点:

[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 = [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 =
   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
points =  
   Empty matrix: 0-by-2

您遇到的问题与如何在计算机上表示有关。关于浮点表示法的更详细讨论出现在我的答案的末尾(“浮点表示法”部分)。TL;DR版本:由于计算机内存有限,数字只能以有限的精度表示。因此,浮点数的精度被限制在一定的小数位数(大约16位有效数字,默认情况下在MATLAB中使用)

实际精度与显示精度 现在来解决问题中的具体例子虽然
24.0000
24.0000
以相同的方式显示,但在这种情况下,它们实际上相差非常小的小数点。你看不到它,因为MATLAB保持了整体显示的整洁。
如果你想看到完整的精度,你应该发出
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

浮点表示法 David Goldberg对浮点数(特别是数字)有一个很好的概述

二进制浮点数实际上由三个整数表示:符号位
s
、有效位(或系数/分数)
b
、指数
e
,每个数字由内存中的64位表示,如下所示:

然后可通过以下公式找到实际值:

此格式允许在10^-308到10^-308范围内表示数字。对于MATLAB,您可以从和中获得这些限制:

由于用于表示浮点数的位数是有限的,因此在上述给定范围内只能表示这么多的有限位数。计算通常会导致一个值与这些有限表示形式中的一个不完全匹配,因此这些值必须舍入。正如上面的例子所讨论的,这些以不同的方式表现出来

为了更好地理解这些舍入错误,查看函数提供的相对浮点精度非常有用,它量化了从给定数字到下一个最大浮点表示的距离:

>> 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
的值从范围
[01]
移动到范围
[10000 10001]
时,计算平均值,然后减去平均偏移量进行比较,我们得到的值与最后3个有效数字不同。这说明了数据的偏移或缩放如何改变对其执行的计算的准确性,这是必须考虑到的某些问题。

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

看看这篇文章:。虽然它的例子是用FORTRAN编写的,但它对几乎所有现代编程语言都有意义,包括MATLAB。“安全比较”部分介绍了您的问题(以及解决方案)。

查看

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

类型

format long g
此命令将显示数字的完整值。可能是24.00000021321!=24.00000123124

试着写

0.1+0.1+0.1==0.3


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

为什么我看不到那个小的小数点?如果你在矩阵视图中查看变量,你就可以看到它。右键点击变量->“视图选择”还是什么?我这里没有MATLAB,所以我无法检查。您也可以通过在命令提示符下键入“format long”来查看细微差异。您是对的:format long points=12.000000000000000 15.000000000000000 33.000000000000000 23.99999999999996 33.000000000000000 24.00000
>> 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
format long g