生成数值序列时的 MATLAB 数值精度

MATLAB numeric precision when generating a numeric sequence

提问人:Steven Ding 提问时间:7/12/2022 最后编辑:Steven Ding 更新时间:7/14/2022 访问量:65

问:

我正在测试这样的操作:

[input] 3.9/0.1 : 4.1/0.1
[output] 39    40

不知道为什么近似为 .如果我添加一个 ,它将按预期进行:4.1/0.140round()

[input] 3.9/0.1 : round(4.1/0.1)
[output] 39    40   41

第一次手术有什么问题?

MATLAB 精度

评论

2赞 Cris Luengo 7/12/2022
请参阅此问答,该问答解释了冒号运算符,而此问答解释了浮点舍入错误。将这两者结合在一起,您应该能够回答您的问题。

答:

1赞 AboAmmar 7/12/2022 #1

浮点数在用固定位数(MATLAB 中默认为 64 位)表示时会损失精度。这是因为有无限数量的实数(即使在 0.0 到 0.1 的小范围内)。另一方面,n 位二进制模式可以表示有限的 2^n 个不同数。因此,并非所有实数都可以表示。将改用最接近的近似值,从而导致准确性损失。

在计算机中,作为 64 位双精度浮点数的最接近的可表示值实际上是,4.1/0.1

4.1/0.1 ≈ 40.9999999999999941713291207...

所以,从本质上讲,这就是你从这个系列中得到的。例如,如果减去 .但是,当您四舍五入时,您会得到最接近预期的值。4.1/0.1 < 41.041 - 4.1/0.1 = 7.105427357601002e-1541.0

根据 IEEE-754 标准的 64 位双精度表示方案:

  • 最高有效位是符号位 (S),0 表示正数,1 表示负数。
  • 以下 11 位表示指数 (E)。
  • 剩下的 52 位代表分数 (F)。

enter image description here

2赞 Cris Luengo 7/12/2022 #2

在本次问答中,我将详细介绍冒号运算符如何在MATLAB中工作以创建范围。但是,此处未涵盖导致此问题中描述的问题的详细信息。

该帖子包含一个函数的完整代码,该函数完全模仿冒号运算符的功能。让我们按照该代码进行操作。我们从 开始,它正好是 39,并且 ,由于舍入误差,它仅略小于 41,并且(如果未给出,则为默认值)。start = 3.9/0.1stop = 4.1/0.1step = 1

它首先计算公差:

tol = 2.0*eps*max(abs(start),abs(stop));

此容差旨在用于在最后一个步骤超过该值时,如果该值在确切的步长数范围内,则仍会使用该值。如果没有容差,就很难使用浮点终点和步长来构建正确的序列。stoptol

但是,然后我们得到这个测试:

if start == floor(start) && step == 1
   % Consecutive integers.
   n = floor(stop) - start;
elseif ...

如果起始值是精确整数,并且步长为 1,则它会强制序列为整数序列。不幸的是,它是通过将步数作为 和 之间的距离来实现的。也就是说,在确定正确的止损点时,它没有使用之前计算的公差!如果略高于整数,则该整数将在范围内。如果略低于整数(如 OP 的情况),则该整数将不属于范围。floor(stop)startstopstop

在这种情况下,MATLAB是否应该将数字四舍五入可能会有争议。 MATLAB选择不这样做。冒号运算符生成的所有序列都使用与用户给出的完全相同的 and 值。它留给用户来确保序列的边界符合要求。stopstartstop

但是,如果冒号运算符没有对整数序列进行特殊处理,那么在这种情况下,结果就不会那么令人惊讶了。让我们在起始值中添加一个非常小的数字,因此它不是一个整数:

>> a = 3.9/0.1 : 4.1/0.1
a =
    39    40

>> b = 3.9/0.1 + eps(39) : 4.1/0.1
b =
   39.0000   40.0000   41.0000