How to raise a number to a power without using the Math. h library?

Asked

Viewed 59,138 times

13

How can I raise a number to a power without using the library math.h?

Example:

potencia = x ^ 1/2;

How do I do it in c++?

  • 1

    Why not use the ready function of libc? You’re doing it for an environment without the libc or is it just an exercise to learn how it works?

  • 1

    Just to understand: why not to use the library math.h?

  • 1

    For integer exponents it is trivial, but for any other case it is a math/numerical analysis problem, practically independent of the language.

  • It’s nothing trivial, but if it helps, follow a link to an implementation: http://www.netlib.org/fdlibm/e_pow.c

8 answers

15

If the goal is to learn how the function works, a good way is to read the source code of some implementation of libc, I will take for example the glibc.

The function powf (internally called __powf) is defined in /math/w_powf.c. Her operation boils down to calling __ieee754_powf who does the actual operation and then deals with edge cases. This second is defined in /sysdeps/ieee754/flt-32/e_powf.c. It’s not easy to read code, but it might be worth the effort. The interesting thing about this code is that it computes in constant time. There are no loops or recursion.

In another implementation, the dietlibc, the function is in the file /libm/pow.c. It has an optimization for integers that computes in a loop. The Pow for integers is calculated as exp(log(mant)*expo), delegating to other functions. The function exp and log are implemented in Assembly using appropriate FPU instructions that do the hardware calculation. See /i386/log.S and /i386/exp.S.

I recommend caution if you intend to implement your own version of any of these functions. Do this only if it is really necessary (you are in an extremely limited resource environment and cannot afford to include a libc with your application). Writing an equivalent function can be very interesting for studies, but not in production.

  • 3

    Put another way: implement Exp(x) and ln(x) using infinite series and then use equality x n = Exp(n * ln(x))

  • @epx, the exp(x) is quite simple to implement with series. But what about the log(x)? I did some tests and could not create a convergent series without limiting the domain. Any suggestions?

  • We can divide x by "and", sometimes until |x|<1, use the Taylor series, and add n to the result.

  • @epx The result did not come out as accurate as I would like. I did something wrong here? Coliru

  • You could iterate to achieve desired accuracy (both in Exp and log), instead of having 11 fixed steps. It’s the first thing I would try.

  • 1

    I published an improved version of your code in response, it worked reasonably well, I would only need to test with extreme limits and calibrate accuracy.

Show 1 more comment

9

Welcome to the world of computational mathematics where various functions such as Pi, square root, sine and cosene are implemented as the summations of infinite convergent series.

There’s a similar issue in Stackoverflow that’s worth reading:

https://stackoverflow.com/questions/2882706/how-can-i-write-a-power-function-myself

It may also be interesting to visit the fast inverse square root wiki that talks about the performance (and less accurate) implementation of the algorithm.

http://en.wikipedia.org/wiki/Fast_inverse_square_root

Remembering that all these algorithms offer values with some degree of accuracy, never an exact value because an "infinite" precision would need infinite processing and infinite memory.

  • 2

    "Infinite" accuracy can be achieved by symbolically representing the values in memory. For example sqrt(2) is guarded as sqrt(2) and not as the result of it. So when that value is squared, the result is perfectly and exactly 2.

  • 1

    We are talking about summing infinite convergent series, not symbolic mathematics. I dare to say that searching symbols in this way is completely outside the scope of the question raised by the OP

  • 1

    It’s completely out yes. I’m just adding.

8

My attempt, based on @Guilherme Bernal. Certainly has problems with extreme values, accumulation of rounding errors etc. etc. but for "well behaved" values seemed to work well.

#include <stdio.h>
#include <math.h>

double epsilon = 1e-15;

double myexp(double x) {
    double old_r = 0;
    double r = 1 + x;
    double div = 1;
    double i = 1;
    double m = x;
    while (fabs(1 - r / old_r) > epsilon) {
       div *= ++i;
       m *= x;
       old_r = r;
       r += m / div;
    }

    return r;
}

double mylog(double x) {
    static const double e = 2.718281828459045;
    int n = 0;
    while (fabs(x) >= 1) {
        x /= e;
        ++n;
    }

    x -= 1;
    double r = x;
    double m = x;
    double old_r = 0;
    double div = 1;
    double signal = 1;
    while (fabs(1 - r / old_r) > epsilon) {
       m *= x;
       old_r = r;
       signal *= -1;
       r += signal * m / ++div;
    }   
    return n+r;
}

int main() {
    printf("exp(1) = %.15f\n", exp(1));
    printf("exp(10) = %.15f\n", exp(10));
    printf("log(exp(10)) = %.15f\n", log(exp(10)));
    printf("sqrt(2) = %.15f\n", exp(0.5*log(2)));
    printf("--\n");
    printf("exp(1) = %.15f\n", myexp(1));
    printf("exp(10) = %.15f\n", myexp(10));
    printf("log(exp(10)) = %.15f\n", mylog(myexp(10)));
    printf("sqrt(2) = %.15f\n", myexp(0.5*mylog(2)));
}

Demonstration: Coliru

exp(1) = 2.718281828459045
exp(10) = 22026.465794806717895
log(exp(10)) = 10.000000000000000
sqrt(2) = 1.414213562373095
--
exp(1) = 2.718281828459046
exp(10) = 22026.465794806710619
log(exp(10)) = 10.000000000000002
sqrt(2) = 1.414213562373096
  • 1

    Just improving your answer, because sqrt(2) != 1.307. I believe you meant sqrt(2) = myexp(0.5*mylog(2))

  • True, the account is wrong. It would have to be Exp(0.5*ln(2)) to be root 2. Fixed program and results.

  • do not know this formula for calculating log. I I answered the same question (had not realized that it was duplicate, thought only related) using the definition of Neperian logarithm through the integral of 1/x dx

  • Easier to use Taylor series, no?

  • It must be, if the person knows how to use... I only knew the name. I had never seen what a series of Taylor was until you mentioned

  • 1

    If you are interested in the subject, there is a log1p() function that calculates the logarithm of (x+1), because the log() function and even my mylog() lose precision when x is close to 1. It is not the fault of the function, but rather how floating point numbers work. https://www.johndcook.com/blog/cpp_log_one_plus_x/

Show 1 more comment

6

The only way to do that is by approaching

(1+x)^(1/2) ~ 1 + x/2 

when x is too small, or creating a routine for the complete series

(1+x)^(1/2) = 1 + x/2 - 3x/4 + 15x/8 - ... 

thing that the sqrt() function already does efficiently. So the only reason not to use it is if you’re studying numerical computing or want to create some better algorithm.

2

Think about how you would do it in a math test. x^(1/2) amounts to sqrt(x). With that in mind, the rest of the implementation is pretty quiet.

Remark: as recalled, in fact x^(1/2) amounts to sqrt(x) and not the 1/x^2, as my colleagues reminded me. The implementation of a square root is a little more complicated than I had planned, but it’s still worth the intention of the initial response - just see how you would implement it (or rather, the algorithm).

  • 2

    In fact, x^(1/2) = sqrt(x)

  • 2

    x^(1/2) NAY is 1/(x^2). That would be x^(-2).

  • 2

    Even sqrt is non-trivial to be implemented. And it seems to me that the exponent 1/2 was just one example, he would be interested in a^b, both may be non-whole.

0

Good afternoon, the program with the function will be like this:

float paw (float x, float y);

main()
{

float a, b, res;

    printf("Entre com o base:");
    scanf("%f", &a);
    printf("\n\nEntre com o expoente:");
    scanf("%f", &b);
    res= paw(a,b);
    printf("O resultado sera:%f\n\n", res);
    system("pause");


}

float paw ( float x, float y)

{

float i;

float a;

if(y== 0 && x!=0)

return 1;

else if(y>=0)

{

a=1;

for( i=0 ; i<=y-1 ; i++)

a = x*a;

return a;

}
else

{

y = -1*y;

a=1;

for( i=0 ; i<=y-1 ; i++)

a = x*a;

a = 1.0/a;

return a;

}

}

0

As simple as possible without the library Math. h

//Tomás Louraco 19/02/18
//Programa que escreve a potencia
#include <iostream>
using namespace std;

int main()
{
setlocale(LC_ALL,"portuguese");
int b, a, i, c=1;
cout << "\t\tIntroduza o valor de A : ";
cin >> a;
cout << "\t\tIntroduza o expoente : ";
cin >> b;
for (i=0;i<=b;i++)
{
c=c*a;
}
cout << "\n\n\n\t\tResultado : " << c;
return 0;
}
  • But the question is to take precisely not whole cases, as 0.5

-1

I imagine it goes something like this:

#include <stdio.h>
main(){
int b,c,d,n,x,y,z;
double p;
printf("Lembrando que: base ^ (numerador/denominador) = potencia\n\n"); //Linha opcional
printf("Digite o valor da base: ");
scanf("%d",&b);
printf("Digite o numerador da fracao do expoente: ");
scanf("%d",&n);
printf("Digite o denominador da fracao do expoente: ");
scanf("%d",&d);
x=1;
z=n;
while (z>0)
{
    z=z-1;
    x=x*b;
}
p=0;
y=0;
while (y<x)
{
    p=p+0.00001;
    c=0;
    y=1;
    while (c<d)
    {
        c=c+1;
        y=y*p;
    }
}
printf("\n\nPotencia: %d ^ (%d/%d) = %.4lf",b,n,d,p);
getchar();
}

Browser other questions tagged

You are not signed in. Login or sign up in order to post.