Matlab数组算术不准确

时间:2012-06-09 19:13:42

标签: arrays matlab floating-point precision floating-accuracy

当我试图在matlab中模拟我的正弦近似时,我发现了一个奇怪的问题。问题是,当我将我的函数应用于数组时,它返回一个结果,而将函数应用于单个值会产生稍微不同的结果。

在这个例子中我能够得到相同的行为:

z = single(0:0.001:1);
F = @(x) (x.^2 - single(1.2342320e-001)).*x.^2;  %some test function

z(999)        % Returns 9.9800003e-001
F(z(999))     % Returns 8.6909407e-001
temp = F(z);  
temp(999)     % Voila! It returns 8.6909401e-001

我也找到了一些东西。一个是第一个结果是正确的(不是后者)。其次,重置术语有时解决问题。所以我不知道如何摆脱它。

5 个答案:

答案 0 :(得分:3)

单精度只有最多7个有意义精度的十进制数字,所以说8.6909407e-001是“正确”而8.6909401e-001是“错误的”在这个坐标中并不是非常有意义。

浮点运算对操作顺序也很敏感,正如您已经发现的那样。当在矩阵而不是标量上运算时,Matlab可能会巧妙地改变计算的顺序。

答案 1 :(得分:1)

对于矢量运算,MatLab的线性代数库可能使用SIMD指令而不是x87 FPU,精度会略有不同。

相对误差非常小,不应破坏任何合理设计的计算。你在测试浮点相等吗?

答案 2 :(得分:1)

我可以保证使用浮点数学的任何设备,实际上,为数学等于表达式提供二进制相等的结果。在各种平台上尝试相应的以下MATLAB:

a = [repmat(1, 10000, 1); 1e16];
format long
sum(a)
sum(flipud(a))

结果:

1.000000000001000e+16
1.000000000000000e+16

加法是可交换的,因此表达式在数学上是等价的。但这个顺序在浮点世界很重要。显然,当累加值大约为0时,按顺序添加10000个1应该对浮点没有问题。但是一旦浮点“浮动”到1e16,1就太小而无法表示,所以它永远不会加入。

这是一个额外的皱纹:x87 FPU在内部以扩展精度(80位)计算。因此,如果编译器决定将中间结果保留在FPU内,FPU代码将为您提供不同的答案。相反,如果它决定将中间结果溢出到堆栈,那么你将回到64.如果它决定使用SSE指令进行计算,那么你将回到64.

在其他答案中提出的这些各种MATLAB技巧可能会让您足够接近您的问题。但是,如果您真的完成了系统的完美建模,那么您可能需要一个更易控制的仿真框架。也许使用vpa,在每一步转换为正确的位数。或者切换到C或C ++,特别注意优化器控件设置。

或者根据输入缩放以数学方式计算错误的界限,并验证答案是否始终低于界限。

答案 3 :(得分:0)

使用单精度数字,两个结果都“相等”(差异小于single类型的相对精度)。以下语句的计算结果为true:

max(abs( arrayfun(F,z) - F(z) )) < eps('single')

修改

如果您真的希望控制它,可以尝试禁用MATLAB加速器,强制它对常规代码和矢量化代码使用相同的执行路径:

feature('jit', 'off')
feature('accel', 'off')
max(abs( arrayfun(F,z) - F(z) ))

feature('jit', 'on')
feature('accel', 'on')
max(abs( arrayfun(F,z) - F(z) ))

分别为第一个/第二个的结果:

ans =
     0
ans =
   5.9605e-08

显然,默认情况下,加速器和即时编译器都已打开。

答案 4 :(得分:0)

好。所有答案都是正确的,但它们并没有解决问题。我想要的是数学上相等的表达式带来二进制相等的结果,无论我是否使用标量或向量算术。结果是我的Cortex-M4,我的计算器以及许多其他程序和设备中的FPU,但不是Matlab。

到目前为止,我看到的唯一解决方案是使用for - 循环而不是矢量算法。丑陋,但它确实有效。

修改

由于回复,我们为此问题提供了更多解决方案。