提问人:Kamran Bigdely 提问时间:3/27/2009 最后编辑:rayryengKamran Bigdely 更新时间:12/10/2017 访问量:25340
为什么在MATLAB中24.0000不等于24.0000?
Why is 24.0000 not equal to 24.0000 in MATLAB?
问:
我正在编写一个程序,我需要删除存储在矩阵中的重复点。问题在于,在检查这些点是否在矩阵中时,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
应从结果中消除两个点 ( 和 )。它应该通过以下命令来完成:vertex1
vertex2
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
答:
您遇到的问题与浮点数在计算机上的表示方式有关。关于浮点表示的更详细讨论出现在我的答案的末尾(“浮点表示”部分)。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 位表示,如下所示:s
b
e
然后可以使用以下公式找到实际值:
此格式允许 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
请注意,当我们将 的值从范围移动到范围时,计算平均值,然后减去平均值偏移量进行比较,我们得到一个最后 3 位有效数字不同的值。这说明了数据的偏移或缩放如何改变对其执行的计算的准确性,这是某些问题必须考虑的问题。x
[0 1]
[10000 10001]
评论
也许这两个数字真的是 24.0 和 24.000000001,但你没有看到所有的小数位。
请看这篇文章:浮点运算的危险。虽然它的示例是用 FORTRAN 编写的,但它几乎适用于任何现代编程语言,包括 MATLAB。您的问题(及其解决方案)在“安全比较”部分中进行了描述。
查看 Matlab EPS 函数。
Matlab 使用浮点数学,精度高达 16 位(仅显示 5 位)。
类型
format long g
此命令将显示数字的 FULL 值。它可能是 24.00000021321 != 24.00000123124。
尝试写作
0.1 + 0.1 + 0.1 == 0.3。
警告:您可能会对结果感到惊讶!
评论
1.2 - 0.2 - 1 == 0
1.2 - 1 - 0.2 == 0