我使用了很棒的Eigen3库来编写MATLAB MEX文件。但即使使用long double
,我也遇到了一些准确性问题(与MATLAB相比)。
最关键的计算似乎是我根据正态分布计算概率的计算。
以下是代码段:
p.fill( 1/( M_PIl * sigma * sigma ) );
p.array() *= ( - 0.5/pow( sigma, 2.0 ) * ( mu.array() - x.array() ).array().square() ).array().exp();
其中x
,p
和mu
为Eigen::Matrix< long double, Dynamic, 1 >
。通常这些向量的长度为3000
。
我可以采取哪些步骤来获得最大可能的精度? 我可以使用哪些正确的GCC编译器标志来尽可能强制80位精度?
P.S:我使用gcc 4.9编译C ++代码(在带有MEX的MATLAB中),我的linux报告了以下可用指令集:Intel MMX,Intel SSE,Intel SSE2,Intel SSE3,Intel SSE4
编辑: 我尝试了下面的@Avi Ginsburg建议并使用以下命令编译它:
mex -g -largeArrayDims '-I/usr/include/eigen3' CXXFLAGS='-DEIGEN_DONT_VECTORIZE -std=c++11 -fPIC' test.cpp
使用double
和long double
,这些选项中的每一个都给出了与MATLAB解决方案相同的错误。
答案 0 :(得分:2)
我在这里猜测。您正在使用SSE指令进行数组计算,最值得注意的是...array().exp()
。我非常确定SSE没有扩展的精度,因此MATLAB和Eigen之间存在差异。
答案 1 :(得分:1)
默认情况下,Eigen使用几种数学函数(包括指数函数)的更快但稍微不准确的实现。您可以通过使用-DEIGEN_FAST_MATH=0
选项进行编译来显式禁用这些优化。
如果您使用gcc作为编译器,请确保不使用-Ofast
或-ffast-math
选项,因为这会导致精度降低。
答案 2 :(得分:1)
如果要计算(1维)normal distribution的概率密度,开头的因子应为1/std::sqrt( 2* M_PIl * sigma * sigma )
。
此外,代码段开头的p.fill()
效率低下。只需将其写在一行:
p = (1/std::sqrt(2*M_PIl*sigma*sigma)) *
( -0.5/(sigma*sigma) * (mu-x).array().square() ).exp();
N.B。:如果您只对阵列执行逐元素操作,请考虑将它们声明为Eigen::Array<...>
而不是Eigen::Matrix<...>
。模板参数是相同的,二进制布局也是相同的,但是每次要进行逐元素操作时都不需要编写.array()
。