当我试图在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
我也找到了一些东西。一个是第一个结果是正确的(不是后者)。其次,重置术语有时解决问题。所以我不知道如何摆脱它。
答案 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
- 循环而不是矢量算法。丑陋,但它确实有效。
修改强>
由于回复,我们为此问题提供了更多解决方案。