繁体   English   中英

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

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

我是一个初学者 C++ 用户,并被赋予了编写一个 function 的任务,它计算一个数字的幂,但我们不允许使用 pow ZC1C425268E68385D1AB5074C17A94F1 或循环。

function 的用户必须在命令 window 中输入底数和指数。

什么是开始的好地方?

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

我们看到一个递归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

递归调用在哪里? 没了! 编译器采用尾调用优化并将递归调用转换为简单循环。 这个汇编代码相当于这个 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在 memory 中用 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 次。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM