15

我想知道是否exp()比更一般的更快pow()。我在 JsPerf http://jsperf.com/pow-vs-exp上运行了快速基准测试,它显示了对我来说很有趣的结果。

Math.exp(logBase * exponent);  // fastest
Math.exp(Math.log(base) * exponent);  // middle
Math.pow(base, exponent);  // slowest

我知道结果在架构和语言上会有很大差异,但我也对理论观点感兴趣。被pow(a, b)实现为exp(log(a) * b)或者是否有一些更聪明的方式如何“直接”(在 C++、C# 或 JavaScript 中)共同计算能力。在某些架构上是否有用于 exp、log 或 pow 的 CPU 指令?

据我所知,两者exp()log()都是使用一些泰勒级数计算的,计算起来非常昂贵。这让我相信,对于恒定的权力基础,这段代码

double logBase = log(123.456);
for (int i = 0; i < 1024; ++i) {
    exp(logBase * 654.321);
}

比这更好

for (int i = 0; i < 1024; ++i) {
    pow(123.456, 654.321);
}

这是正确的假设吗?

4

3 回答 3

17

是的,exp会比pow一般情况下更快。

exp和功能将log针对目标平台进行优化;可以使用许多技术,例如 Pade 逼近、线性或二进制归约,然后进行逼近等。

pow功能一般会按照你说的来实现exp(log(a) * b),所以明显比exp单独的要慢。有许多特殊情况,pow例如负指数、整数指数、等于 1/2 或 1/3 的指数等。在一般情况下,这些情况会进一步减慢,pow因为这些测试很昂贵。

请参阅上的这个 SO 问题pow

于 2013-07-26T23:06:24.260 回答
4

无论架构细节如何,Math.pow都必须在错误检查方面做更多的事情(例如,如果基数为负会发生什么?)。比Math.exp(因此我希望pow会慢一些)。

规范的相关部分:

http://ecma-international.org/ecma-262/5.1/#sec-15.8.2.8

15.8.2.8 exp(x)

返回 x 的指数函数的依赖于实现的近似值(e 的 x 次方,其中 e 是自然对数的底)。

如果 x 为 NaN,则结果为 NaN。如果 x 为 +0,则结果为 1。如果 x 为 -0,则结果为 1。如果 x 为 +∞,则结果为 +∞。如果 x 为 -∞,则结果为 +0。

http://ecma-international.org/ecma-262/5.1/#sec-15.8.2.13

15.8.2.13 pow (x, y)

返回将 x 提高到 y 次方的结果的依赖于实现的近似值。

如果 y 为 NaN,则结果为 NaN。如果 y 为 +0,则结果为 1,即使 x 为 NaN。如果 y 为 -0,则结果为 1,即使 x 为 NaN。如果 x 为 NaN 且 y 为非零,则结果为 NaN。如果 abs(x)>1 且 y 为 +∞,则结果为 +∞。如果 abs(x)>1 且 y 为 -∞,则结果为 +0。如果 abs(x)==1 且 y 为 +∞,则结果为 NaN。如果 abs(x)==1 且 y 为 -∞,则结果为 NaN。如果 abs(x)<1 且 y 为 +∞,则结果为 +0。如果 abs(x)<1 且 y 为 -∞,则结果为 +∞。如果 x 为 +∞ 且 y>0,则结果为 +∞。如果 x 为 +∞ 且 y<0,则结果为 +0。如果 x 为 -∞ 且 y>0 且 y 为奇数,则结果为 -∞。如果 x 为 -∞ 且 y>0 且 y 不是奇数,则结果为 +∞。如果 x 为 -∞ 且 y<0 且 y 为奇数,则结果为 -0。如果 x 为 -∞ 且 y<0 且 y 不是奇数,则结果为 +0。如果 x 为 +0 且 y>0,结果是+0。如果 x 为 +0 且 y<0,则结果为 +∞。如果 x 为 -0 且 y>0 且 y 为奇数,则结果为 -0。如果 x 为 -0 且 y>0 且 y 不是奇数,则结果为 +0。如果 x 为 -0 且 y<0 且 y 为奇数,则结果为 -∞。如果 x 为 -0 且 y<0 且 y 不是奇数,则结果为 +∞。如果 x<0 且 x 是有限的且 y 是有限的且 y 不是整数,则结果为 NaN。

于 2013-07-26T23:29:01.363 回答
1

作为部分答案,在某些架构上有 exp、log 或 pow 的说明是的。然而,这并不一定意味着什么。

例如,在 x86 上有

  • f2xm1计算 2 x - 1
  • fscale计算 y * 2 (int)x
  • fyl2x计算 y * log 2 x
  • fyl2xp1计算 y * log 2 (x + 1) (对输入范围有限制)

但是,它们的使用并不多。它因架构而异,但它们从来都不是很快。作为一个更极端的例子,fyl2x在 Sandy Bridge 上有 724 的延迟(最近!),在那个时候在同一个处理器上你可以做大约 700 个独立的浮点加法,或者大约 240 个相关的浮点加法,或者大约 2000 个独立的简单整数运算。

这几乎是最糟糕的,但它们通常很慢。足够慢,以至于手动实现可以击败它们,或者至少不会明显失败。

此外,FPU 代码正在慢慢消失,取而代之的是 SSE 代码。没有这些指令的 SSE 等效项。

于 2013-07-26T22:45:58.670 回答