我是C ++的初学者,已经被赋予编写计算数字幂的函数的任务,但是我们不允许使用pow函数或循环。
该函数的用户必须在命令窗口中输入基数和指数。
什么是开始的好地方?
答案 0 :(得分:7)
pow(x, y)
可以写为exp(y * log(x))
。据我所知,它满足问题的约束条件。
对于实数x
和y
,要做到这一点很难。当然,有一些愚蠢的选择对积分y
使用递归,但对线性问题使用递归从来都不是一种特别好的方法。
答案 1 :(得分:4)
void start_here(unsigned int n) {
if (n > 0)
start_here(n - 1);
}
start_here(2019);
然后您编写:
double pow(double x, unsigned int exp) {
if (exp > 0)
return x * pow(x, exp - 1);
else
return 1;
}
然后您可以改善:
double pow(double x, unsigned int exp) {
if (exp > 0) {
const auto p2 = pow(x, exp / 2);
return p2 * p2 * ((exp & 1) ? x : 1);
}
else
return 1;
}
最后一种算法称为二进制幂运算。
最后,您将学习模板:
template<unsigned int exp>
constexpr double pow(double x) {
if constexpr (exp > 0) {
const auto p2 = pow<exp / 2>(x);
return p2 * p2 * ((exp & 1) ? x : 1);
}
else
return 1;
}
编辑。尾部递归优化。
让我们看一下为第一个版本生成的汇编代码
pow()
,没有优化(-O0
):
pow(double, unsigned int):
push rbp
mov rbp, rsp
sub rsp, 16
movsd QWORD PTR [rbp-8], xmm0
mov DWORD PTR [rbp-12], edi
cmp DWORD PTR [rbp-12], 0
je .L2
mov eax, DWORD PTR [rbp-12]
lea edx, [rax-1]
mov rax, QWORD PTR [rbp-8]
mov edi, edx
movq xmm0, rax
call pow(double, unsigned int)
mulsd xmm0, QWORD PTR [rbp-8]
jmp .L3
.L2:
movsd xmm0, QWORD PTR .LC0[rip]
.L3:
leave
ret
.LC0:
.long 0
.long 1072693248
我们看到了递归call pow(double, unsigned int)
。
现在让我们添加一些优化(-O2 -ffast-math
):
pow(double, unsigned int):
movapd xmm1, xmm0
movsd xmm0, QWORD PTR .LC0[rip]
test edi, edi
je .L4
.L3:
mulsd xmm0, xmm1
sub edi, 1
jne .L3
ret
.L4:
ret
.LC0:
.long 0
.long 1072693248
递归调用在哪里?它消失了!编译器使用tail call optimization并将递归调用转换为简单循环。此汇编代码等效于此C ++:
double pow(double x, unsigned int exp) {
double p = 1;
if (exp == 0)
return p;
loop:
p *= x;
if (--exp > 0)
goto loop;
return p;
}
由于没有浮点乘法的关联性,因此如果没有-ffast-math
option,则无法进行优化。
最后,1.d
在内存中由8个字节表示:
3F F0 00 00 | 00 00 00 00 (base 16)
转换为两个long
数字后,它们变为:
1072693248 | 0 (base 10)
这是两个可以在汇编代码中找到的魔术数字。
答案 2 :(得分:0)
这是您的操作方式。
int exponent(int , int );
int main()
{
cout<<exponent(3,8);
return 0;
}
int exponent(int x , int y )
{
if(y==0)
return 1;
return (x*exponent(x,y-1));
}
让我知道您是否难以消化。
答案 3 :(得分:0)
使用递归的解决方案:
double power(double x, double n, double product = 1) {
if (n <= 0) return product;
product *= x;
return power(x, n - 1, product);
}
int main()
{
cout << power(10, 2);
}
答案 4 :(得分:0)
假设基数和指数是不可或缺的,那为什么不做循环展开呢。假设sizeof(int)= 4字节= 32位。
long long power (int base, int exponent)
{
long long result = 1;
long long powOf2 = base;
if (exponent%2)
result *= powOf2;
exponent >>= 1;
powOf2 *= powOf2;
if (exponent%2)
result *= powOf2;
exponent >>= 1;
powOf2 *= powOf2;
(copy paste 30 more times)
return result;
}
如果sizeof(int)= 8,则复制粘贴62次而不是30次。