Matlab精度:简单减法不为零

发布于 2024-12-12 00:42:36 字数 120 浏览 0 评论 0原文

我在 Matlab 上计算了这个简单的和:

2*0.04-0.5*0.4^2 = -1.387778780781446e-017

但结果不为零。我能做些什么?

I compute this simple sum on Matlab:

2*0.04-0.5*0.4^2 = -1.387778780781446e-017

but the result is not zero. What can I do?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

感情旳空白 2024-12-19 00:42:36

阿巴兹和吉姆·克莱对所发生的事情有很好的解释。

通常情况下,您真正​​想要的是检查 2*0.04 和 0.5*0.4^2 之间的差异是否足够小,而不是精确计算 2*0.04 - 0.5*0.4^2 的值。在相关数值精度范围内。如果是这种情况,您可以检查是否 abs(2*0.04 - 0.5*0.4^2) abs(2*0.04 - 0.5*0.4^2) < ;脱粒。这里的 thresh 可以是任意小的数字,也可以是包含 eps 的表达式,它给出了您正在使用的数字类型的精度。

编辑:
感谢吉姆和塔尔提出的改进建议。更改为将差异的绝对值与阈值进行比较,而不是差异。

Aabaz and Jim Clay have good explanations of what's going on.

It's often the case that, rather than exactly calculating the value of 2*0.04 - 0.5*0.4^2, what you really want is to check whether 2*0.04 and 0.5*0.4^2 differ by an amount that is small enough to be within the relevant numerical precision. If that's the case, than rather than checking whether 2*0.04 - 0.5*0.4^2 == 0, you can check whether abs(2*0.04 - 0.5*0.4^2) < thresh. Here thresh can either be some arbitrary smallish number, or an expression involving eps, which gives the precision of the numerical type you're working with.

EDIT:
Thanks to Jim and Tal for suggested improvement. Altered to compare the absolute value of the difference to a threshold, rather than the difference.

泪痕残 2024-12-19 00:42:36

Matlab 使用双精度浮点数来存储实数。这些数字的形式为 m*2^e,其中 m2^522^53 之间的整数code>(尾数),e 是指数。如果一个数字是这种形式,我们就将其称为浮点数。

计算中使用的所有数字都必须是浮点数。通常,这可以完全完成,就像表达式中的 20.5 一样。但对于其他数字,尤其是大多数小数点后有数字的数字,这是不可能的,必须使用近似值。在这种情况下发生的情况是该数字被四舍五入到最接近的浮点数。

因此,每当您在 Matlab 中编写类似 0.04 的内容时,您实际上是在说“给我最接近 0.04 的浮点数。 > 在您的表达式中,有 2 个数字需要近似:0.040.4

此外,浮点数的加法和乘法等运算的确切结果 。可能不是浮点数尽管它的形式始终是m*2^e,但尾数可能太大,因此

在一天结束时, 您会因四舍五入而产生额外的错误。像你这样的表达式将偏离操作数大小的大约 2^-52 倍,或者大约 10^-17

总而言之:你的表达式不计算为零的原因有两个:

  1. 你开始的一些数字 。与 不同(近似值)为您提供的确切数字。
  2. 中间结果也可能是确切结果的近似值。

Matlab uses double-precision floating-point numbers to store real numbers. These are numbers of the form m*2^e where m is an integer between 2^52 and 2^53 (the mantissa) and e is the exponent. Let's call a number a floating-point number if it is of this form.

All numbers used in calculations must be floating-point numbers. Often, this can be done exactly, as with 2 and 0.5 in your expression. But for other numbers, most notably most numbers with digits after the decimal point, this is not possible, and an approximation has to be used. What happens in this case is that the number is rounded to the nearest floating-point number.

So, whenever you write something like 0.04 in Matlab, you're really saying "Get me the floating-point number that is closest to 0.04. In your expression, there are 2 numbers that need to be approximated: 0.04 and 0.4.

In addition, the exact result of operations like addition and multiplication on floating-point numbers may not be a floating-point number. Although it is always of the form m*2^e the mantissa may be too large. So you get an additional error from rounding the results of operations.

At the end of the day, a simple expression like yours will be off by about 2^-52 times the size of the operands, or about 10^-17.

In summary: the reason your expression does not evaluate to zero is two-fold:

  1. Some of the numbers you start out with are different (approximations) to the exact numbers you provided.
  2. The intermediate results may also be approximations of the exact results.
强辩 2024-12-19 00:42:36

您看到的是量化误差。 Matlab 使用双精度数来表示数字,虽然它们具有很高的精度,但它们仍然不能表示所有实数,因为实数的数量是无限的。我不确定 Aabaz 的技巧,但总的来说,我会说除了将您的输入修改为双友好数字之外,您无能为力。

What you are seeing is quantization error. Matlab uses doubles to represent numbers, and while they are capable of a lot of precision, they still cannot represent all real numbers because there are an infinite number of real numbers. I'm not sure about Aabaz's trick, but in general I would say there isn't anything you can do, other than perhaps massaging your inputs to be double-friendly numbers.

没有伤那来痛 2024-12-19 00:42:36

我不知道它是否适用于您的问题,但通常最简单的解决方案是扩展数据。

例如:

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

编辑:当然我并不是想为此类问题提供通用的解决方案,但这仍然是一个很好的实践,可以使您避免数值计算中的一些问题(曲线拟合, ETC ...)。请参阅 Jim Clay 的回答,了解您遇到这些问题的原因。

I do not know if it is applicable to your problem but often the simplest solution is to scale your data.

For example:

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

EDIT: of course I did not mean to give a universal solution to these kind of problems but it is still a good practice that can make you avoid a few problems in numerical computation (curve fitting, etc ...). See Jim Clay's answer for the reason why you are experiencing these problems.

臻嫒无言 2024-12-19 00:42:36

我很确定这是一个古老的浮点精度问题。

您需要 1e-17 精度吗?这仅仅是想要“漂亮”输出的情况吗?
在这种情况下,您可以只使用格式化的 sprintf 来显示您想要的有效位数。

请注意,这不是 matlab 问题,而是数字如何以二进制表示的基本限制。

为了好玩,算出二进制中的 .1 是什么...

一些参考:
http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems
http://www.mathworks.com/support/tech-notes/1100/1108.html

I'm pretty sure this is a case of ye olde floating point accuracy issues.

Do you need 1e-17 accuracy? Is this merely a case of wanting 'pretty' output?
In that case, you can just use a formatted sprintf to display the number of significant digits you want.

Realize that this is not a matlab problem, but a fundamental limitation of how numbers are represented in binary.

For fun, work out what .1 is in binary...

Some references:
http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems
http://www.mathworks.com/support/tech-notes/1100/1108.html

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文