我在Matlab上计算这个简单的总和:
2*0.04-0.5*0.4^2 = -1.387778780781446e-017
但结果不是零。我能做什么?
答案 0 :(得分:4)
Aabaz和Jim Clay对正在发生的事情有很好的解释。
通常情况下,你真正想要的是检查2 * 0.04和0.5 * 0.4 ^ 2之间的差异,而不是精确计算2 * 0.04 - 0.5 * 0.4 ^ 2的值。足以在相关的数值精度范围内。如果是这种情况,那么您可以检查是否2*0.04 - 0.5*0.4^2 == 0
,而不是检查是否abs(2*0.04 - 0.5*0.4^2) < thresh
。这里thresh
可以是一些任意的小数字,或者是一个涉及eps
的表达式,它给出了你正在使用的数字类型的精度。
编辑: 感谢Jim和Tal提出的改进意见。改变以将差异的绝对值与阈值进行比较,而不是差异。
答案 1 :(得分:2)
Matlab使用双精度浮点数来存储实数。这些是m*2^e
形式的数字,其中m
是2^52
和2^53
之间的整数(尾数),e
是指数。如果它是这种形式,让我们将一个数字称为浮点数。
计算中使用的所有数字必须是浮点数。通常,这可以完全完成,就像表达式中的2
和0.5
一样。但对于其他数字,最值得注意的是大多数小数点后的数字,这是不可能的,必须使用近似值。在这种情况下会发生的是数字四舍五入到最接近的浮点数。
所以,每当你在Matlab中写0.04
之类的东西时,你真的会说“给我一个最接近0.04
的浮点数。在你的表达式,有2个数字需要近似:0.04
和0.4
。
此外,浮点数上的加法和乘法等操作的确切结果可能不是浮点数。虽然它始终是m*2^e
形式,但尾数可能太大。因此,您可以通过舍入操作结果来获得额外的错误。
在一天结束时,像你这样的简单表达将比操作数大约2 ^ -52倍,或大约10 ^ -17。
总结:你的表达式没有评估为零的原因是双重的:
答案 2 :(得分:1)
我不知道它是否适用于您的问题,但通常最简单的解决方案是扩展您的数据。
例如:
a=0.04;
b=0.2;
a-0.2*b
ans=-6.9389e-018
c=a/min(abs([a b]));
d=b/min(abs([a b]));
c-0.2*d
ans=0
编辑:当然我并不是要为这类问题提供通用解决方案,但它仍然是一个很好的做法,可以让你避免数值计算中的一些问题(曲线拟合,等...)。请参阅Jim Clay的答案,了解您遇到这些问题的原因。
答案 3 :(得分:1)
您所看到的是quantization error。 Matlab使用双精度来表示数字,虽然它们具有很高的精度,但它们仍然不能代表所有实数,因为存在无数个实数。我不确定Aabaz的伎俩,但总的来说,我会说你没有什么可以做的,除了可能按摩你的输入是双重友好的数字。
答案 4 :(得分:1)
我很确定这是一个浮点精度问题。
您需要1e-17的准确度吗?这只是一个想要“漂亮”输出的情况吗? 在这种情况下,您可以使用格式化的sprintf来显示您想要的有效位数。
意识到这不是matlab问题,而是数字以二进制表示的基本限制。
为了好玩,找出.1是二进制的......
一些参考文献: http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems http://www.mathworks.com/support/tech-notes/1100/1108.html