Loren关于MATLAB的艺术

将想法转化为matlab

一个更快更准确的总和

今天的Guest Blogger是Christine Tobler,谁是MathWorks的开发人员,正在研究核心数字函数。
大家好!我想告诉你们一个关于舍入错误的故事,这个算法在 ,以及兼容性问题。在过去的几年里,我的同事程博比(Bobby Cheng)对 让它更准确和更快,我觉得在这里讲这个故事会很有趣。

总数问题

即使是一个简单的函数 ,浮点号码我们将它们施加的顺序:
X = (1 + 1e-16) + 1e-16
x = 1
Y = 1 + (1e-16 + 1e-16)
Y = 1.0000.
x - y
ans = -2.2204 e-16
结果是 命令取决于输入相加的顺序。的 MATLAB中的命令从最直接的实现开始:从第一个数字开始,然后一次一个地加起来。我已经实现了 Sumoriginal. 在这篇文章的底部。
但我们遇到了一个问题:对于单精度来说,结果有时是错误的:
sumOfOnes = sumOriginal(ones(1e8, 1, 1))'单身的'))
sumOfOnes =单16777216
这是怎么回事?问题是,对于这个数字,四舍五入错误大于1,我们可以通过调用看到 每股收益
EPS(Sumofones)
Ans = single 2
因此,再加一个1,然后四舍五入到单个精度会再次返回相同的数字,因为精确的结果会向下四舍五入以适应单个精度:
Sumofones + 1
Ans =单16777216
这一点在 用于计算一个大数组的均值,因为计算的均值可能小于输入数组的所有元素。

计算总和的不同方式

现在我们已经在MATLAB中拥有一个线程版本的总和,在那里我们将并行计算数组的多个块的总和,然后将其添加到最后。事实证明,此版本没有同一问题:
numBlocks = 8;
sumInBlocks(的(1 e8, 1'单身的'),numblocks)
Ans =单个100000000
我们在MATLAB中做了这个改变 甚至对于非线程的情况,它解决了这种情况和其他类似的情况。但请记住,虽然这在很多情况下是一种改进,但它不是一个完美的算法,仍然不会总是给出正确的答案。我们可以修改我们将输入分割成的块的数量,但并不是所有的工作都很好:
sumInBlocks(的(1 e8, 1'单身的'), 4)
ans =单身67108864
sumInBlocks(的(1 e8, 1'单身的'), 128年)
Ans = single 99999832
让我们看看另一个案例,我们不仅仅是总结第一的情况(仍然每次总结相同的数字,因为它使得与确切值更容易比较)。我们将在这里查看结果的相对误差,它比我们获得精确整数更正的更为重要:
X = repmat(single(3155), 54194,1);
exactSum = 3155 * 54194;
numblocks = 2。^(0:9)
numBlocks = 1×10
12 4 8 16 32 64 128 256 512
err = 0(1,长度(numBlocks));
i = 1:长度(numBlocks)
/ / / / / / / / / / / /
结束
重对数(numBlocks犯错)
包含('块数'
ylabel (' sumInBlocks函数中的相对错误'
因此,在选择确切的方块数量时,这无疑是一种平衡行为,上面的图表只是代表一个数据集。
还有其他可能的算法来计算和,我在底部包括了一些链接。一些更复杂的问题会导致计算时间的放缓,这将给没有遇到上述准确性问题的情况带来负担。
我们选择了“按块计算”算法,因为可以以这样的方式进行更改,该总和变得更快。这是使用循环展开完成的:而不是一次添加一个号码,而是添加多个数字以同时分离运行总数。我已经包括一个实施 sumloopunrolled. 以下。
顺便说一下,我并没有明确给出选择 numBlocks 我们做了,因为我们不想让任何人依赖它——毕竟它将来可能会再次改变。

更改

在R2017B中,我们首先发布了这个新版本 ,仅为单精度编号的情况。通过逐叠单个数据阵列的所有实际问题已经解决,并且该功能甚至同时获得更快。但是,我们也得到了对行为变化不满意的人的一些反馈;虽然新的行为给出了良好或更好的结果,但他们的代码依靠旧行为并调整新行为是痛苦的。
虽然我们不想被困在永不改善我们的功能时,无论是害怕打破依赖,那么我们肯定希望使得尽可能地更新痛苦的过程。一般而言,我们的目标是为了运行运行的再现性:如果用相同的输入调用两次函数,则输出将是相同的。但是,如果机器,操作系统或MATLAB版本(以及其他外部)的变化,这也可以在圆截止错误中更改MATLAB返回的确切值。例如,矩阵乘法的输出是常见的,以改变性能的缘故。但是 在美国,很长一段时间没有任何改变,所以越来越多的人依赖于它的确切行为,这超出了我们的预期。
在做了这个更改之后 对于单个值,我们等待几个版本来评估客户对更改的反馈。在R2020b中,我们为所有其他数据类型添加了相同的行为。这一次,我们添加了一个 发行公告 这描述了性能改进和提到的变化作为“兼容性考虑”。虽然我们仍然有一些反馈意见,但不方便的变化,但它比第一次案例少。
在新的MATLAB版本中,你是否有过舍入错误的改变?你觉得发布说明中的兼容性考虑有用吗?请让我们知道 在评论里

关于精确计算和的参考文献

辅助功能

函数s = sumOriginal (x)
s = 0;
2 = 1:长度(x)
S = S + x(ii);
结束
结束
函数s = sinblocks(x,numblocks)
len =长度(x);
blockLength = ceil(len / numBlocks);
s = sumoriginal(x(1:blocklength));
iter = blockLength;
iter <莱恩
s = s + sumOriginal(x(iter+1:min(iter+blockLength, len))));
iter = iter + blockLength;
结束
结束
函数s = sumLoopUnrolled (x)% #好< DEFNU >
% numBlocks == 4的循环展开的例子。为了简单起见,我们假设
% x的长度能被4整除。
%请注意,使用内置代码的右方会更快
%编译器标志。Matlab代码中不一定更快。
s1 = 0;
s2 = 0;
S3 = 0;
s4 = 0;
II = 1:4:长度(x)
s1 = s1 + x(ii);
S2 = S2 + x(ii+1);
S3 = S3 + X(II + 2);
(ii+3);
结束
S = s1 + s2 + s3 + s4;
结束
版权所有2021 MathWorks,Inc。
|
  • 打印
  • 发送电子邮件

コメント

コメントを残すには,ここをクリックしてMATHWORKSアカウントアカウントサインインするか新闻MATHWORKSアカウントを作品成します。