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

时间:2009-03-26 16:10:39

标签: matlab floating-point precision

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

6 个答案:

答案 0 :(得分:90)

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

实际与显示精度

现在要解决问题中的具体示例... 24.000024.0000显示的方式相同,事实证明它们实际上是在这种情况下,十进制数非常小。你没有看到它,因为MATLAB only displays 4 significant digits by default,保持整体显示整洁。如果你想看到完整的精度,你应该发出format long命令或查看{数字{3}}:

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

初始化值与计算值

由于只能为浮点数表示有限数量的值,因此计算可能会导致值落在其中两个表示之间。在这种情况下,结果必须四舍五入到其中一个。这引入了一个小的hexadecimal representation。这也意味着直接或通过某些计算初始化值可能会产生略微不同的结果。例如,值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),:)

浮点表示

David Goldberg对浮点数(特别是machine-precision error)的概述是IEEE 754 standard for floating-point arithmetic

二进制浮点数实际上由三个整数表示:符号位s,有效数(或系数/分数)b和指数eWhat Every Computer Scientist Should Know About Floating-Point Arithmetic,每个数字由内存中的64位表示如下:

For double-precision floating-point format

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

enter image description here

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

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

由于存在用于表示浮点数的有限数量的比特,因此只有很多有限数可以在上述给定范围内表示。计算通常会导致一个值与这些有限表示中的一个不完全匹配,因此必须舍入值。这些realmax以不同的方式表现出来,如上例所述。

为了更好地理解这些舍入误差,查看函数machine-precision errors提供的相对浮点精度很有用,它可以量化从给定数字到下一个最大浮点表示的距离。 :

>> 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位有效数据有所不同。这说明了数据的偏移或缩放如何改变对其执行的计算的准确性,这是某些问题必须考虑的因素。

答案 1 :(得分:22)

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

答案 2 :(得分:13)

format long g

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

答案 3 :(得分:7)

尝试写作

  

0.1 + 0.1 + 0.1 == 0.3。

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

答案 4 :(得分:2)

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

答案 5 :(得分:1)

查看Matlab EPS function

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