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


68

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

结果中应消除两个点(vertex1vertex2)。应该通过以下命令完成:

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

1
这也得到了解决这里
ChrisF

2
@Kamran:抱歉,当您问到比较其他问题中的值时,我没有指出浮点比较的危险。我没有立即想到,您可能会遇到该问题。
gnovice

2
作为旁注,比较1.2 - 0.2 - 1 == 01.2 - 1 - 0.2 == 0。令人惊讶,不是吗?在处理浮点数时,操作顺序很重要。
jub0bs 2014年

1
@Tick Tock:作为问题的作者,我什至不明白您为问题选择的标题。同样,它没有反映出当您打印出变量时MATLAB不会显示数字的整个浮点部分的事实。
卡姆兰·比格德利

1
@ m7913d,我知道了。但通常他们在较新的问题上贴上“重复”标签。:请阅读重复标签规则meta.stackexchange.com/questions/10841/...
卡姆兰Bigdely

Answers:


98

您遇到的问题与计算机上浮点数的表示方式有关。在我的答案(“浮点表示”部分)的结尾处出现了有关浮点表示的更详细的讨论。的TL; DR版本:因为计算机具有有限数量的存储器,数字只能用有限精度表示。因此,浮点数的精度被限制为一定的小数位数(双精度值约为16个有效数字,这是MATLAB中的默认)。

实际与显示精度

现在,以解决具体的示例中的问题......24.000024.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从范围移动[0 1]到范围时[10000 10001],计算平均值,然后减去平均值偏移量进行比较,我们得到的值对于最后3个有效数字是不同的。这说明了数据的偏移量或缩放比例如何改变对其执行的计算的准确性,这是某些问题必须解决的问题。


为什么看不到这么小的十进制数?
Kamran Bigdely 09年

2
如果在矩阵视图中查看变量,则可以看到它。右键单击变量->“视图选择”或其他内容?我这里没有MATLAB,所以无法检查。
2009年

4
您还可以通过在命令提示符下键入“长格式”来看到小的差异。
gnovice

2
您是对的:格式长点= 12.000000000000000 15.000000000000000 33.000000000000000 23.999999999999996 33.000000000000000 24.000000000000000
Kamran Bigdely 2009年

6
在这里,“格式化十六进制”有时甚至比格式化还可以提供更多帮助。
山姆·罗伯茨

23

看这篇文章:浮点的危险。尽管其示例在FORTRAN中,但对于几乎任何现代编程语言(包括MATLAB)都具有意义。“安全比较”部分介绍了您的问题(及其解决方案)。


1
我前一段时间发现了它,并给它留下了深刻的印象=)现在,我总是在类似情况下推荐它。
罗里克

这个优秀资源的存档版本
wizclown

13

类型

format long g

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


7

尝试写作

0.1 + 0.1 + 0.1 == 0.3。

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


我试过了,它返回0。但是我看不到它有什么用,上面的问题。你能给我解释一下吗?
最多

6
这是因为0.1带有一些浮点错误,并且当您将三个这样的项加在一起时,这些错误不一定加起来为0。同一问题也导致(浮动)24不完全等于(另一个浮动)24。 。
德里克

2

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


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.