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

Why is 24.0000 not equal to 24.0000 in MATLAB?

提问人:Kamran Bigdely 提问时间:3/27/2009 最后编辑:rayryengKamran Bigdely 更新时间:12/10/2017 访问量:25340

问:

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

评论

2赞 gnovice 3/27/2009
@Kamran:对不起,当你在另一个问题中问到比较值时,我没有指出浮点比较的危险。我没有立即想到您可能会遇到这个问题。
1赞 ChrisF 3/27/2009
这里也谈到了这一点
2赞 jub0bs 10/12/2014
顺便说一句,比较 和 .令人惊讶,不是吗?在处理浮点数时,运算顺序很重要。1.2 - 0.2 - 1 == 01.2 - 1 - 0.2 == 0
1赞 Kamran Bigdely 8/19/2016
@Tick Tock:作为问题的作者,我甚至无法理解你为我的问题选择的标题。此外,它没有反映这样一个事实,即当您打印出变量时,MATLAB不会显示数字的整个浮点部分。
1赞 m7913d 5/4/2017
Matlab 中浮点比较最佳实践的可能重复
1赞 Kamran Bigdely 5/5/2017
@m7913d,我明白了。但通常他们会在较新的问题上贴上“重复”标签。请阅读重复标签的规则:meta.stackexchange.com/questions/10841/...

答:

100赞 gnovice 3/27/2009 #1

您遇到的问题与浮点数在计算机上的表示方式有关。关于浮点表示的更详细讨论出现在我的答案的末尾(“浮点表示”部分)。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

将显示“Equal!”。

然后,您可以将代码更改为以下代码:

points = points((abs(points(:,1)-vertex1(1)) > tolerance) | ...
                (abs(points(:,2)-vertex1(2)) > tolerance),:)

浮点表示

对浮点数(特别是浮点运算的 IEEE 754 标准)的一个很好的概述是 David Goldberg 的 What Every Computer Scientist Should Know About Floating-Point Arithmetic

二进制浮点数实际上由三个整数表示:一个符号位、一个有效数(或系数/分数)和一个指数。对于双精度浮点格式,每个数字由内存中的 64 位表示,如下所示:sbe

enter image description here

然后可以使用以下公式找到实际值:

enter image description here

此格式允许 10^-308 到 10^308 范围内的数字表示。对于 MATLAB,您可以从 realminrealmax 获得以下限制:

>> 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

请注意,当我们将 的值从范围移动到范围时,计算平均值,然后减去平均值偏移量进行比较,我们得到一个最后 3 位有效数字不同的值。这说明了数据的偏移或缩放如何改变对其执行的计算的准确性,这是某些问题必须考虑的问题。x[0 1][10000 10001]

评论

0赞 Kamran Bigdely 3/27/2009
为什么我看不到那么小的小数额?
2赞 atsjoo 3/27/2009
如果在矩阵视图中查看变量,则可以看到它。右键单击变量 -> “查看选择”或其他什么?我这里没有MATLAB,所以我无法检查。
5赞 gnovice 3/27/2009
您还可以通过在命令提示符下键入“format long”来查看细微的差异。
2赞 Kamran Bigdely 3/27/2009
你是对的: 格式长点 = 12.0000000000000000 15.000000000000000 33.000000000000000 23.999999999999996 33.000000000000000 24.000000000000000
7赞 Sam Roberts 10/5/2009
在这里,“格式十六进制”有时比格式化长更有帮助。
2赞 Jimmy J 3/27/2009 #2

也许这两个数字真的是 24.0 和 24.000000001,但你没有看到所有的小数位。

23赞 Rorick 3/27/2009 #3

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

评论

1赞 Rorick 3/27/2009
我前段时间发现了它,并给它留下了深刻的印象=)现在我总是在类似的情况下推荐它。
0赞 wizclown 7/12/2018
这个优秀资源的存档版本
1赞 jle 3/27/2009 #4

查看 Matlab EPS 函数

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

13赞 KitsuneYMG 3/27/2009 #5

类型

format long g

此命令将显示数字的 FULL 值。它可能是 24.00000021321 != 24.00000123124

7赞 Andrey Rubshtein 12/15/2011 #6

尝试写作

0.1 + 0.1 + 0.1 == 0.3。

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

评论

1赞 Max 9/16/2015
我试过了,它返回 0。但我看不出它与上面的问题有什么关系。你能给我解释一下吗?
7赞 Derek 3/4/2016
这是因为 0.1 会带来一些浮点误差,当您将三个这样的项相加时,误差加起来不一定等于 0。同样的问题导致(浮动)24 不完全等于(另一个浮动)24。