从我的一个朋友那里,我听说pow函数比它的等价物慢,只需简单地将基数乘以其指数的次数。例如,根据他的说法,
#include <stdio.h>
#include <math.h>
int main () {
double e = 2.71828
e2 = pow (e, 2.0)
printf("%le", e2)
}
慢于
#include <stdio.h>
int main() {
double e = 2.71828
e2 = e * e
printf("%le", e2)
}
作为一个新手,我认为他们都以相同的速度编译,并且按照相同的逻辑,我更喜欢前者的典型精神。那么,为什么前一段代码比后一段慢?
答案 0 :(得分:5)
pow(double,double)
需要处理提升任何权力,而不仅仅是基于整数的权力,尤其是2
。因此,它比仅仅进行两个双值的简单乘法要复杂得多。
答案 1 :(得分:3)
因为pow
函数必须实现一个更通用的算法,该算法必须适用于所有情况(特别是,它必须能够升级到{{>> 指数可由{表示{1}}),而double
只是一个简单的乘法,可归结为一个或两个汇编指令。
但是,如果编译器足够智能,它可能会自动将e*e
自动替换为pow(e, 2.0)
(实际上,在您的情况下,它可能只是在编译时执行整个计算)。
为了好玩,我运行了一些测试:编译以下代码
e*e
使用#include <math.h>
double pow2(double value)
{
return pow(value, 2.);
}
double knownpow2()
{
double e=2.71828;
return pow(e, 2.);
}
double valuexvalue(double value)
{
return value*value;
}
double knownvaluexvalue()
{
double e=2.71828;
return e*e;
}
(g ++ 4.7.3)并使用g++ -O3 -c pow.c
反汇编输出我得到:
objdump -d -M intel pow.o
因此,在编译器已经知道所涉及的所有值的情况下,它只是在编译时执行了计算;对于0000000000000000 <_Z4pow2d>:
0: f2 0f 59 c0 mulsd xmm0,xmm0
4: c3 ret
5: 66 66 2e 0f 1f 84 00 data32 nop WORD PTR cs:[rax+rax*1+0x0]
c: 00 00 00 00
0000000000000010 <_Z9knownpow2v>:
10: f2 0f 10 05 00 00 00 movsd xmm0,QWORD PTR [rip+0x0] # 18 <_Z9knownpow2v+0x8>
17: 00
18: c3 ret
19: 0f 1f 80 00 00 00 00 nop DWORD PTR [rax+0x0]
0000000000000020 <_Z11valuexvalued>:
20: f2 0f 59 c0 mulsd xmm0,xmm0
24: c3 ret
25: 66 66 2e 0f 1f 84 00 data32 nop WORD PTR cs:[rax+rax*1+0x0]
2c: 00 00 00 00
0000000000000030 <_Z16knownvaluexvaluev>:
30: f2 0f 10 05 00 00 00 movsd xmm0,QWORD PTR [rip+0x0] # 38 <_Z16knownvaluexvaluev+0x8>
37: 00
38: c3 ret
和pow2
,它发出了一个valuexvalue
(即在两种情况下,它都归结为单个汇编指令中值与其自身的乘法)。
答案 2 :(得分:0)
Here is one (simple, heed the comment) pow implementation。在通用中,它涉及许多分支,一个潜在的分支,并调用exp,log,modf ..
另一方面,乘法是在大多数高CPU上的单个指令(给予或接受)。