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

时间:2019-11-14 16:25:44

标签: c++ function loops exponent

我是C ++的初学者,已经被赋予编写计算数字幂的函数的任务,但是我们不允许使用pow函数或循环。

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

什么是开始的好地方?

5 个答案:

答案 0 :(得分:7)

pow(x, y)可以写为exp(y * log(x))。据我所知,它满足问题的约束条件。

对于实数xy,要做到这一点很难。当然,有一些愚蠢的选择对积分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次。