C++在不使用pow或循环的情况下计算一个数字的幂

C++ calculate the power of a number without using pow or loop

本文关键字:一个 数字 计算 情况下 pow C++ 循环      更新时间:2023-10-16

我是一名C++初学者,有一项任务是编写一个计算数字幂的函数,但我们不允许使用pow函数或循环。

函数的用户必须在命令窗口中输入基数和指数。

从什么地方开始?

pow(x, y)可以写成exp(y * log(x))。据我所知,这满足了问题的限制。

对于真正的xy,任何替代方案都是困难的。当然,对积分y使用递归有一些愚蠢的选择,但对线性问题使用递归从来都不是一个特别好的方法。

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

我们看到一个递归CCD_ 8。

现在让我们添加一些优化(-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

递归调用在哪里?它不见了!编译器采用尾调用优化,并将递归调用转换为简单循环。这个汇编代码相当于这个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选项,这种优化是不可能的。

最后,1.d在内存中用8个字节表示:

3F F0 00 00 | 00 00 00 00  (base 16)

转换成两个long数字后,它们变为:

1072693248  | 0            (base 10)   

这是两个可以在汇编代码中发现的神奇数字。

以下是您的操作方法。

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));
}

如果你觉得难以消化,请告诉我。

使用递归的解决方案:

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);
}

假设基数和指数为整数,为什么不直接进行循环展开呢。假设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次。