std :: pow()非常慢,基数非常接近1

fle*_*ser 5 c++ performance glibc pow

我有一个解码方程的数字代码,f(x) = 0我必须提高x功率p.我用一堆东西解决它,但最后我有牛顿的方法.解决方案恰好相同x = 1,因此是我的问题的原因.当迭代解决方案接近时1,比如x = 1 + 1e-13,计算所需的时间std::pow(x, p)大大增加,很容易达到100倍,使我的代码无法使用.

运行这个东西的机器是CentOS上的AMD64(Opteron 6172),命令很简单y = std::pow(x, p);.类似的行为出现在我的所有机器上,全部是x64.正如这里所记录的,这不仅是我的问题(也就是说,其他人也很生气),只出现在x64上,而且仅用于x接近1.0.类似的事情发生了exp.

解决这个问题对我来说至关重要.有没有人知道是否有任何解决这种缓慢的方法?

编辑:约翰指出,这是由于非正规.问题是,如何解决这个问题?代码是C++,编译后g++使用GNU Octave.看来,虽然我已经设置CXXFLAGS,包括-mtune=native-ffast-math,这是没有帮助和代码运行一样慢.

现在的解决方案:对于所有关心这个问题的人,下面提出的解决方案对我个人而言并不适用.我真的需要通常的速度std::pow(),但没有周围的迟钝x = 1.我个人的解决方案是使用以下hack:

inline double mpow(double x, double p) __attribute__ ((const));

inline double mpow(double x, double p)
{
    double y(x - 1.0);
    return (std::abs(y) > 1e-4) ? (std::pow(x, p)) : (1.0 + p * y * (1.0 + (p - 1.0) * y * (0.5 + (1.0 / 6.0) * (p - 2.0) * y)));
}
Run Code Online (Sandbox Code Playgroud)

可以改变界限,但是对于-40 <p <40,误差小于约1e-11,这足够好.我发现的开销很小,因此解决了我的问题.

eca*_*mur 9

显而易见的解决方法是在实数中注意,a ** b == exp(log(a) * b)并使用该形式.您需要检查它是否对结果的准确性产生不利影响.编辑:正如所讨论的那样,这也会受到几乎同样程度放缓的影响.

问题不是非正规,至少不是直接的; 试图计算exp(-2.4980018054066093e-15)遭受同样的减速,而-2.4980018054066093e-15肯定不是正常的.

如果您不关心结果的准确性,那么缩放exponend或exponent应该会让您超出慢速区域:

sqrt(pow(a, b * 2))
pow(a * 2, b) / pow(2, b)
...
Run Code Online (Sandbox Code Playgroud)

glibc维护者知道这个错误:http://sourceware.org/bugzilla/show_bug.cgi? id = 13932 - 如果你正在寻找修复而不是解决方法,你想要委托一个浮点数学专家有开源经验.


duf*_*ymo 0

这也可能是你的算法。也许改用 BFGS 之类的方法而不是牛顿方法会有所帮助。

您没有提及您的收敛标准。也许这些也需要调整。