8

I was reading through How can I write a power function myself? and the answer given by dan04 caught my attention mainly because I am not sure about the answer given by fortran, but I took that and implemented this:

#include <iostream>
using namespace std;
float pow(float base, float ex){
    // power of 0
    if (ex == 0){
        return 1;
    // negative exponenet
    }else if( ex < 0){
        return 1 / pow(base, -ex);
    // even exponenet
    }else if ((int)ex % 2 == 0){
        float half_pow = pow(base, ex/2);
        return half_pow * half_pow;
    //integer exponenet
    }else{
        return base * pow(base, ex - 1);
    }
}
int main(){
    for (int ii = 0; ii< 10; ii++){\
        cout << "pow(" << ii << ".5) = " << pow(ii, .5) << endl;
        cout << "pow(" << ii << ",2) = " << pow(ii,  2) << endl;
        cout << "pow(" << ii << ",3) = " << pow(ii,  3) << endl;
    }
}

though I am not sure if I translated this right because all of the calls giving .5 as the exponent return 0. In the answer it states that it might need a log2(x) based on a^b = 2^(b * log2(a)), but I am unsure about putting that in as I am unsure where to put it, or if I am even thinking about this right.

NOTE: I know that this might be defined in a math library, but I don't need all the added expense of an entire math library for a few functions.

EDIT: does anyone know a floating-point implementation for fractional exponents? (I have seen a double implementation, but that was using a trick with registers, and I need floating-point, and adding a library just to do a trick I would be better off just including the math library)

Community
  • 1
  • 1
gardian06
  • 1,496
  • 3
  • 20
  • 34
  • 3
    Or the added expense of a FPU? – Ignacio Vazquez-Abrams Mar 11 '12 at 04:51
  • 2
    you're missing the fractional exponent (the code there is for an even exponent) - looking at the original link, i think you are copying from something that only supports integer exponents (hence tests for fractional will fail). – andrew cooke Mar 11 '12 at 04:51
  • @andrewcooke dang it. could you help me out by showing the needed changes – gardian06 Mar 11 '12 at 04:52
  • 13
    I assure you that this is more expensive than math.h's pow. – Pubby Mar 11 '12 at 04:52
  • @pubby It may not be more expensive than math.h's pow, see http://stackoverflow.com/questions/9272155/replacing-extrordinarily-slow-pow-function – tpg2114 Mar 11 '12 at 04:53
  • i don't see any answer in that thread that describes how to do a fractional exponent. – andrew cooke Mar 11 '12 at 04:54
  • So if I call pow(base, 1000000), this function has a recursive depth of 1000000? Ouch! What if there are NaN arguments, or +/-Inf, or denormals? – Brett Hale Mar 11 '12 at 05:10
  • 1
    @BrettHale it's log2(n) isn't it? so for pow(base, 1000000) it's a depth of about 20. – andrew cooke Mar 11 '12 at 05:13
  • 1
    @andrewcooke - sure, if ((float) ex % 2 == 0) actually worked. – Brett Hale Mar 11 '12 at 05:23
  • Step debugging it shows that it counts 0.5 as an even number. Thusly it calls itself recursively, each time dividing the exponent in half, down to .0625/2 (in my build) before a comparison with 0 is reached and it returns a 1, which is then multiplied by itself all the way back up the stack. It is apparently doing an epsilon comparison with 0, good for it. If it weren't, it would iterate many many more times until it underflowed. I'm not sure exactly what mathematical flaw this points out, it just gives a simple reason for the failure of .5 . – std''OrgnlDave Mar 22 '12 at 20:33
  • AFAIK, when linking to a static library, only the code relevant to the functions that are actually called in a program gets incorporated to the executable file, so I don't think you should worry about the expense of the full math library - if you make sure your project is linking to it statically. –  Mar 28 '12 at 16:00
  • There is a deleted answer with 15 upvotes and positive comments. It said - Below are links to real implementations of powf. I expect simpler solutions would lack accuracy in the result or not handle InF and NaN parameters. http://opensource.apple.com/source/Libm/Libm-2026/Source/Intel/expf_logf_powf.c http://opensource.apple.com/source/Libm/Libm-315/Source/ARM/powf.c – Brian Swift Jun 20 '20 at 22:11

7 Answers7

8

I have looked at this paper here which describes how to approximate the exponential function for double precision. After a little research on Wikipedia about single precision floating point representation I have worked out the equivalent algorithms. They only implemented the exp function, so I found an inverse function for the log and then simply did

    POW(a, b) = EXP(LOG(a) * b).

compiling this gcc4.6.2 yields a pow function almost 4 times faster than the standard library's implementation (compiling with O2).

Note: the code for EXP is copied almost verbatim from the paper I read and the LOG function is copied from here.

Here is the relevant code:

    #define EXP_A 184
    #define EXP_C 16249 

    float EXP(float y)
    {
      union
      {
        float d;
        struct
        {
    #ifdef LITTLE_ENDIAN
          short j, i;
    #else
          short i, j;
    #endif
        } n;
      } eco;
      eco.n.i = EXP_A*(y) + (EXP_C);
      eco.n.j = 0;
      return eco.d;
    }

    float LOG(float y)
    {
      int * nTemp = (int*)&y;
      y = (*nTemp) >> 16;
      return (y - EXP_C) / EXP_A;
    }

    float POW(float b, float p)
    {
      return EXP(LOG(b) * p);
    }

There is still some optimization you can do here, or perhaps that is good enough. This is a rough approximation but if you would have been satisfied with the errors introduced using the double representation, I imagine this will be satisfactory.

SirGuy
  • 10,660
  • 2
  • 36
  • 66
5

I think the algorithm you're looking for could be 'nth root'. With an initial guess of 1 (for k == 0):

#include <iostream>
using namespace std;


float pow(float base, float ex);

float nth_root(float A, int n) {
    const int K = 6;
    float x[K] = {1};
    for (int k = 0; k < K - 1; k++)
        x[k + 1] = (1.0 / n) * ((n - 1) * x[k] + A / pow(x[k], n - 1));
    return x[K-1];
}

float pow(float base, float ex){
    if (base == 0)
        return 0;
    // power of 0
    if (ex == 0){
        return 1;
    // negative exponenet
    }else if( ex < 0){
        return 1 / pow(base, -ex);
    // fractional exponent
    }else if (ex > 0 && ex < 1){
        return nth_root(base, 1/ex);
    }else if ((int)ex % 2 == 0){
        float half_pow = pow(base, ex/2);
        return half_pow * half_pow;
    //integer exponenet
    }else{
        return base * pow(base, ex - 1);
    }
}
int main_pow(int, char **){
    for (int ii = 0; ii< 10; ii++){\
        cout << "pow(" << ii << ", .5) = " << pow(ii, .5) << endl;
        cout << "pow(" << ii << ",  2) = " << pow(ii,  2) << endl;
        cout << "pow(" << ii << ",  3) = " << pow(ii,  3) << endl;
    }
    return 0;
}

test:

pow(0, .5) = 0.03125
pow(0,  2) = 0
pow(0,  3) = 0
pow(1, .5) = 1
pow(1,  2) = 1
pow(1,  3) = 1
pow(2, .5) = 1.41421
pow(2,  2) = 4
pow(2,  3) = 8
pow(3, .5) = 1.73205
pow(3,  2) = 9
pow(3,  3) = 27
pow(4, .5) = 2
pow(4,  2) = 16
pow(4,  3) = 64
pow(5, .5) = 2.23607
pow(5,  2) = 25
pow(5,  3) = 125
pow(6, .5) = 2.44949
pow(6,  2) = 36
pow(6,  3) = 216
pow(7, .5) = 2.64575
pow(7,  2) = 49
pow(7,  3) = 343
pow(8, .5) = 2.82843
pow(8,  2) = 64
pow(8,  3) = 512
pow(9, .5) = 3
pow(9,  2) = 81
pow(9,  3) = 729
CapelliC
  • 59,646
  • 5
  • 47
  • 90
  • sorry had other projects to work on. this crashes when given say a `.3` – gardian06 Mar 22 '12 at 01:46
  • I tried adding .3, seems to work (I'm using GCC 4.5). but see the edit for the *base* == 0... – CapelliC Mar 22 '12 at 07:18
  • @chac: Did you literally try `1/3`? Because that is 0 because of integer division. To make it float division, you need to use `1./3.` instead (actually one of the dots is already sufficient). – celtschk Mar 24 '12 at 20:43
  • @celtschk: I tried with float, but the approach it's fundamentally flawed, losing precision for the integer conversion performed by 1/ex at nth_root(base, 1/ex); – CapelliC Mar 24 '12 at 21:11
  • the nth_root mathematics only works for roots. in the context of .5 the root is 2, and in the context of .3 this is a combination of power, and root being power of 3, and root of 10. I think in order for the nth_root to be considered fully you would first have to break the fraction made by the decimal into its power, and root components – gardian06 Mar 25 '12 at 15:20
  • 1
    Thank you very much. I use this on Pebble develop – zszen Jan 05 '17 at 04:49
  • This still fails with examples such as `74088 ^ (1 / 3)`... but thanks anyways – Lapys Sep 17 '18 at 11:05
  • I've added a modified version as https://stackoverflow.com/a/73479424/210151 that should handle such cases. – Treviño Aug 24 '22 at 21:04
4

I think that you could try to solve it by using the Taylor's series, check this. http://en.wikipedia.org/wiki/Taylor_series

With the Taylor's series you can solve any difficult to solve calculation such as 3^3.8 by using the already known results such as 3^4. In this case you have 3^4 = 81 so

3^3.8 = 81 + 3.8*3( 3.8 - 4) +..+.. and so on depend on how big is your n you will get the closer solution of your problem.

AlexTheo
  • 4,004
  • 1
  • 21
  • 35
  • Taylor series comes to a question of curve fitting, and requires knowing an appropriate number of terms to use to generate an accurate value. to few and you can easily hit the constant increase/decrease, and to many, and your wasting processes. is there a reasonable suggestion on the k number of the summation? – gardian06 Mar 25 '12 at 08:10
  • You could compare the result of the last calculation in the series with some standard number like 0.0000001 so this is a limit of accuracy. So you have to make it as iteration as a sum and check the calculation of each iteration if the final value of it is < than 0.000001 for example you will break the iteration. – AlexTheo Mar 25 '12 at 14:18
  • The Taylor series is only approximated at a single point where as a minimax algorithm such as (chebyshev, remez) are over a bound, that gives optimal equi-ripple error. – nimig18 Apr 07 '17 at 06:17
1

I and my friend faced similar problem while we're on an OpenGL project and math.h didn't suffice in some cases. Our instructor also had the same problem and he told us to seperate power to integer and floating parts. For example, if you are to calculate x^11.5 you may calculate sqrt(x^115, 10) which may result more accurate result.

Cihad Turhan
  • 2,749
  • 6
  • 28
  • 45
  • this would require a larger exponentiation to start with. though it may simplify having to calculate floating point powers it would also increase the number of recursive, or loop calls made within the power function (considering that unless bit math is being done it needs some kind of loop) – gardian06 Mar 25 '12 at 08:05
0

Reworked on @capellic answer, so that nth_root works with bigger values as well.

Without the limitation of an array that is allocated for no reason:

#include <iostream>

float pow(float base, float ex);

inline float fabs(float a) {
    return a > 0 ? a : -a;
}

float nth_root(float A, int n, unsigned max_iterations = 500, float epsilon = std::numeric_limits<float>::epsilon()) {
    if (n < 0)
        throw "Invalid value";

    if (n == 1 || A == 0)
        return A;

    float old_value = 1;
    float value;
    for (int k = 0; k < max_iterations; k++) {
        value = (1.0 / n) * ((n - 1) * old_value + A / pow(old_value, n - 1));

        if (fabs(old_value - value) < epsilon)
            return value;

        old_value = value;
    }

    return value;
}

float pow(float base, float ex) {
    if (base == 0)
        return 0;

    if (ex == 0){
        // power of 0
        return 1;
    } else if( ex < 0) {
        // negative exponent
        return 1 / pow(base, -ex);
    } else if (ex > 0 && ex < 1) {
        // fractional exponent
        return nth_root(base, 1/ex);
    } else if ((int)ex % 2 == 0) {
        // even exponent
        float half_pow = pow(base, ex/2);
        return half_pow * half_pow;
    } else {
        // integer exponent
        return base * pow(base, ex - 1);
    }
}

int main () {
    for (int i = 0; i <= 128; i++) {
        std::cout << "pow(" << i << ", .5) = " << pow(i, .5) << std::endl;
        std::cout << "pow(" << i << ", .3) = " << pow(i, .3) << std::endl;
        std::cout << "pow(" << i << ",  2) = " << pow(i,  2) << std::endl;
        std::cout << "pow(" << i << ",  3) = " << pow(i,  3) << std::endl;
    }

    std::cout << "pow(" << 74088 << ",  .3) = " << pow(74088,  .3) << std::endl;

    return 0;
}
Treviño
  • 2,999
  • 3
  • 28
  • 23
0

This solution of MINE will be accepted upto O(n) time complexity utpo input less then 2^30 or 10^8 IT will not accept more then these inputs It WILL GIVE TIME LIMIT EXCEED warning but easy understandable solution

#include<bits/stdc++.h>
using namespace std;

double recursive(double x,int n)
{
    // static is important here
    // other wise it will store same  values while multiplying 
    
    double p = x;
    
    double ans;
   
    // as we multiple p it will multiply it with q which has the 
    //previous value of this ans latter we will update the q
    // so that q has fresh value for further test cases here 
    
  static double q=1; // important 
    
    if(n==0){ ans = q; q=1; return ans;}
   
    if(n>0)
    {
        p *= q;
        // stored value got multiply by p
        q=p;
        // and again updated to q 
        p=x;
        //to update the value to the same value of that number 
        
        
        // cout<<q<<" ";
        recursive(p,n-1);
        
    }

    return ans;
}

class Solution {
public:
    double myPow(double x, int n) {
        

        
        // double q=x;double N=n;
        // return pow(q,N);
        
        
        // when both sides are double this function works 
        
        
        if(n==0)return 1;
        
   
         x = recursive(x,abs(n));
        
 
        
        if(n<0) return double(1/x);
        
        // else 
        return x;
        
    }
};

For More help you may try LEETCODE QUESTION NUMBER 50

0

**NOW the Second most optimize code pow(x,n) ** logic is that we have to solve it in O(logN) so we devide the n by 2 when we have even power n=4 , 4/2 is 2 means we have to just square it (22)(22) but when we have odd value of power like n=5, 5/2 here we have square it to get also the the number itself to it like (22)(2*2)*2 to get 2^5 = 32 HOPE YOU UNDERSTAND FOR MORE YOU CAN VISIT POW(x,n) question on leetcode below the optimized code and above code was for O(n) only *

#include<bits/stdc++.h>
using namespace std;
double recursive(double x,int n)
{
   
 // recursive calls will return the whole value of the program at every calls  

    if(n==0){return 1;}
    
    // 1 is multiplied when the last value we get as we don't have to multiply further 
    
    double store;
    store = recursive(x,n/2);
    
    // call the function after the base condtion you have given to  it here
    
    if(n%2==0)return store*store;
    else
    {
        return store*store*x;
        // odd power we have the perfect square multiply the value;
    }

}

// main function or the function for indirect call to recursive function 
 double myPow(double x, int n) {

        if(n==0)return 1;
        
         x = recursive(x,abs(n));
     
        // for negatives powers
        
        if(n<0) return double(1/x);
        // else for positves 
        return x;    
    }