洛伦谈MATLAB的艺术

把想法变成MATLAB

更快,更准确的总和

今天的嘉宾博客是恭托布勒,谁是在核心数值函数工作MathWorks公司的开发人员。
大家好!我想给大家讲一个关于舍入误差的故事,舍入误差是在 ,以及兼容性问题。在过去几年中,我的同事Bobby Cheng对 为了让它更准确、更快,我想在这里讲一个有趣的故事。

总数中的数字问题

即使对于一个简单的函数,如 与浮点数中,我们总结起来事项的顺序:
x=(1+1e-16)+1e-16
x=1
y=1+(1e-16+1e-16)
Y = 1.0000
x-y
ans=-2.2204e-16
因此,调查的结果 命令取决于输入相加的顺序。这个 MATLAB中的命令以最直接的实现开始:从第一个数字开始,一次加一个数字。我已经在中国实施了这一计划 sumOriginal 在这篇文章的底部。
但我们遇到了一个问题:对于单精度,结果有时是非常错误的:
sumOfOnes=原始的sumOfOnes(1e8,1,'单身的'))
Sumofone=单个16777216
这里发生了什么?问题是对于这个数字,舍入误差大于1,正如我们通过调用 EPS
EPS(sumOfOnes)
ans=单个2
因此,再加一个1,然后四舍五入到单精度,会再次返回相同的数字,因为精确的结果会向下四舍五入到单精度:
sumOfOnes + 1
ans=单个16777216
这一点在以下情况下尤为明显: 用于计算大型数组的平均值,因为计算的平均值可能小于输入数组的所有元素。

计算总和的不同方法

现在我们已经有了MATLAB中sum的线程版本,我们将并行计算一个数组的几个块的和,然后将它们相加。结果表明,此版本没有相同的问题:
numBlocks=8;
Suminblock(一个),'单身的'),numBlocks)
ans=单个100000000
我们在MATLAB的 即使对于非线程的情况,它也解决了这个和其他类似的情况。但请记住,虽然这在许多情况下是一种改进,但它不是一个完美的算法,也不会总是给出正确的答案。我们可以修改将输入分割成的块的数量,但并非所有块都很好:
Suminblock(一个),'单身的'), 4)
ANS = 67108864单
Suminblock(一个),'单身的'), 128)
ans=单个9999832
让我们看看,我们不只是在总结头号的情况下(还是总结相同数量的每次,因为它可以更容易地与精确值进行比较)。我们将着眼于这里的结果,这是比我们得到确切的整数正确更具相关性的相对误差:
x=repmat(单个(3155),54194,1);
exactSum=3155*54194;
numBlocks = 2 ^(0:9)。
麻木= 1×10
1 2 4 8 16 32 64 128 256 512
err=零(1,长度(numBlocks));
对于i=1:长度(数量)
ERR(ⅰ)= ABS(sumInBlocks(X,numBlocks(I)) - exactSum)/ exactSum;
终止
日志(numBlocks,err)
xlabel(“块数”)
伊拉贝尔(“sumInBlocks函数中的相对错误”)
因此,在选择块的确切数量时,肯定存在一种平衡行为,上面的图只表示一个数据集。
还有其他可能的算法来计算总和,我在底部包括了一些链接。一些更复杂的问题将导致计算时间的减慢,这将给没有遇到上述准确度问题的案例带来负担。
我们选择了这个“用块计算”算法,因为变化可能在一个这样的方式做出总和变得更快。相反,在每次添加一个号码,多个号码被添加到运行总计在同一时间分开:这是使用循环展开来完成。我已经包括在执行 sumLoopUnrolled 以下。
顺便说一句:我并没有明确给出选择的权利 麻木 我们做出了决定,因为我们不希望任何人依赖于此——毕竟,未来可能会再次发生变化。

改变

在R2017b,我们首次发布的新版本 ,因为只有单精度数的情况下。与结果是关闭的幅度为单一大型数据阵列的所有实际问题已得到解决,并且功能甚至得到了在同一时间更快。然而,我们也得到了相当长的一段反馈人谁不满的行为改变;而新的行为给予一样好或更好的结果,他们的代码是依靠旧的行为,并适应新的行为是痛苦的。
虽然我们不希望被卡住在从来没有能够改善我们的功能,生怕打破了依赖,我们一定要进行更新,尽可能做到无痛苦的过程。一般来说,我们的目标是运行到重现性:如果一个函数与相同的输入叫了两声,输出将是相同的。但是,如果机器,操作系统或MATLAB版本(其他的外部之间)的变化,这也可以更改设定为通过MATLAB返回的确切值,内舍入误差。这是常见的矩阵乘法的输出变化对性能的缘故,例如。但随着 ,很长一段时间没有改变,因此更多的人开始依赖它的精确行为,这超出了我们的预期。
所以在对 对于单个值,我们等待了几个版本来评估客户对更改的反馈。在R2020b中,我们为所有其他数据类型添加了相同的行为。这一次,我们添加了一个 发行公告 描述的性能提升都和提到的改变为“兼容性代价”。虽然我们仍然对这个是不方便的变化一些反馈,它是小于在第一种情况。
在新的MATLAB发行版中,您是否对舍入错误进行了更改?是否发现发行说明中的兼容性注意事项有用?请告知我们 在评论中 .

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

辅助函数

作用s=原始(x)
S = 0;
对于ii=1:长度(x)
s=s+x(ii);
终止
终止
作用S = sumInBlocks(X,numBlocks)
len=长度(x);
blockLength=celi(len/numBlocks);
S = sumOriginal(X(1:块长度));
ITER =块长度;
虽然iter
s=s+s(x(iter+1:min(iter+blockLength,len));
iter=iter+区块长度;
终止
终止
作用s=未展开(x)%#好的
%numBlocks==4的循环展开示例。为了简单起见,我们假设
%x的长度可以被4整除。
%
%注意:使用带有右键的内置代码时,此技术速度更快
%的编译器标志。它不一定会快于这样的MATLAB代码。
s1=0;
s2=0;
S3 = 0;
s4=0;
对于II = 1:4:长度(X)
S1 = S1 + X(ⅱ);
s2=s2+x(ii+1);
S3 = S3 + X(ⅱ+ 2);
s4=s4+x(ii+3);
终止
s=s1+s2+s3+s4;
终止
版权所有2021 MathWorks公司
|

댓글

댓글을 남기려면링크를 클릭하여 数学作品계정에 로그인하거나 계정을 새로 만드십시오.