1/sqrt(x)和std::exp(-0.5 * std::log(x))之间的数值权衡

4
我发现了一些旧代码,计算的是:

double y = 1 / std::sqrt(x);

使用:

constexpr double base16 = 16.0;
double log_base16 = std::log(base16);
double y = std::pow(base16, -0.5 * std::log(x) / log_base16);

这实质上是:

double y = std::exp(-0.5 * std::log(x));

这些方法在数值方面是否有任何合理的优势(如准确性或更容易避免下溢/上溢)?原作者可能认为有。


1
它可以有多久的历史?constexpr在2011年被标准化。 - Potatoswatter
@Potatoswatte,我清理了语法,使示例对人们尽可能清晰明了。 - keith
2个回答

2
原始代码在现代C++标准和IEEE754浮点数中都应被视为非常淘气,因为IEEE标准要求std::sqrt是精确的。此外,std::pow没有这样的要求。因此,我倾向于将其重写为1 / std::sqrt(x),当然需要测试。参考:http://en.cppreference.com/w/cpp/numeric/math/sqrt

请注意,“精确”可能会有代价。理论上,替代代码可能更快。话虽如此,这是非常理论性的。唯一快速的对数是以2为底数及其幂次方,指数也是如此。2^(log2(x)/2) 可能比旧代码快得多。 - MSalters
@MSalters 当然,使用异或运算可能更快,但对于大多数值来说,它的准确性会降低。 - Yakk - Adam Nevraumont
@Yakk:std::exp2(std::log2(x)/2))的速记。 - MSalters
1
@MSalters 不,^在C++中是异或。加油! ;) - Yakk - Adam Nevraumont

0

我看不出用pow()和log()来实现sqrt()有什么特别好的理由。可能是sqrt()的实现中存在错误,这是一种解决方法。

总的来说,我认为这种实现方式会更慢、更不精确。


1
鉴于 1 / std::sqrt(x),我相信我们可以假设 x>0 - MSalters

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接