【问题标题】:Why is 24.0000 not equal to 24.0000 in MATLAB?为什么 24.0000 在 MATLAB 中不等于 24.0000?
【发布时间】:2010-10-15 17:54:07
【问题描述】:

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

【问题讨论】:

  • 这也已解决here
  • @Kamran:对不起,当您在其他问题中询问比较值时,我没有指出浮点比较的危险。我没有立即想到你可能会遇到这个问题。
  • 附带说明,比较1.2 - 0.2 - 1 == 01.2 - 1 - 0.2 == 0。令人惊讶,不是吗?处理浮点数时,运算顺序很重要。
  • @Tick Tock:作为问题的作者,我什至无法理解您为我的问题选择的标题。它也没有反映出当您打印出变量时,MATLAB 没有显示数字的整个浮点部分。
  • @m7913d,我明白了。但通常他们会在较新的问题上贴上“重复”标签。请阅读重复标签规则:meta.stackexchange.com/questions/10841/…

标签: matlab floating-point precision


【解决方案1】:

您遇到的问题与floating-point numbers 在计算机上的表示方式有关。浮点表示的更详细讨论出现在我的答案的末尾(“浮点表示”部分)。 TL;DR 版本:因为计算机的内存量是有限的,所以数字只能以有限的精度表示。因此,浮点数的精度被限制在一定的小数位(double-precision values 大约 16 位有效数字,MATLAB 中使用的默认值)。

实际与显示的精度

现在解决问题中的具体示例... 虽然24.000024.0000 以相同的方式显示,但事实证明它们实际上相差很小在这种情况下小数。你看不到它是因为 MATLAB only displays 4 significant digits by default,保持整体显示整洁。如果你想看到完整的精度,你应该发出format long 命令或查看hexadecimal representation号码:

>> pi
ans =
    3.1416
>> format long
>> pi
ans =
   3.141592653589793
>> num2hex(pi)
ans =
400921fb54442d18

初始化值与计算值

由于浮点数只能表示有限数量的值,因此计算结果可能会导致介于这两种表示之间的值。在这种情况下,结果必须四舍五入到其中之一。这引入了一个小的machine-precision error。这也意味着直接或通过一些计算初始化一个值可能会产生略有不同的结果。例如,值 0.1 没有 exact 浮点表示(即它被略微四舍五入),因此由于方式不同,您最终会得到这样的反直觉结果舍入误差累积:

>> 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),:)

浮点表示

David Goldberg 的 What Every Computer Scientist Should Know About Floating-Point Arithmetic 是浮点数(特别是 IEEE 754 standard for floating-point arithmetic)的一个很好的概述。

二进制浮点数实际上由三个整数表示:符号位s、有效数(或系数/小数)b 和指数eFor double-precision floating-point format,每个数字用64位表示,在内存中布局如下:

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

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

>> realmin
ans =
    2.225073858507201e-308
>> realmax
ans =
    1.797693134862316e+308

由于用于表示浮点数的位数是有限的,因此在上述给定范围内可以表示的有限数只有这么多。计算通常会产生一个与这些有限表示之一不完全匹配的值,因此必须对这些值进行四舍五入。这些machine-precision errors 以不同的方式表现自己,如上述示例中所述。

为了更好地理解这些舍入误差,查看函数 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 个有效值不同的值位数。这说明了数据的偏移或缩放如何改变对其执行的计算的准确性,这是某些问题必须考虑的问题。

【讨论】:

  • 为什么我看不到这么小的小数?
  • 在矩阵视图中查看变量就可以看到。右键单击变量->“查看选择”还是什么?我这里没有 MATLAB,所以无法查看。
  • 您还可以通过在命令提示符下键入“format long”来查看细微差别。
  • 你是对的:格式长点= 12.000000000000000 15.000000000000000 33.000000000000000 23.999999999999996 33.000000000000000 24.00000
  • "format hex" 有时比格式化 long 更有帮助。
【解决方案2】:

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

【讨论】:

  • 前段时间发现了它,印象非常深刻=) 现在我总是在类似的情况下推荐它。
  • Archived version 这个优秀的资源!
【解决方案3】:

类型

format long g

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

【讨论】:

    【解决方案4】:

    试着写

    0.1 + 0.1 + 0.1 == 0.3。

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

    【讨论】:

    • 我试过了,它返回 0。但我不明白它与上述问题有什么关系。你能给我解释一下吗?
    • 这是因为 0.1 带有一些浮点错误,当您将三个这样的项相加时,错误加起来不一定是 0。同样的问题导致 (floating) 24 不是完全等于(另一个浮动)24。
    【解决方案5】:

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

    【讨论】:

      【解决方案6】:

      查看Matlab EPS function

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-10-18
        相关资源
        最近更新 更多