4

I am not sure if power by squaring takes care of negative exponent. I implemented the following code which works for only positive numbers.

    #include <stdio.h>
    int powe(int x, int exp)
    {
         if (x == 0)
            return 1;
         if (x == 1)
            return x;
         if (x&1)
                return powe(x*x, exp/2);
         else
                return x*powe(x*x, (exp-1)/2);       
    }

Looking at https://en.wikipedia.org/wiki/Exponentiation_by_squaring doesn't help as the following code seems wrong.

    Function exp-by-squaring(x, n ) 
      if n < 0  then return exp-by-squaring(1 / x, - n );
      else if n = 0  then return  1;
      else if n = 1  then return  x ; 
      else if n is even  then return exp-by-squaring(x * x,  n / 2);
      else if n is odd  then return x * exp-by-squaring(x * x, (n - 1) / 2).

Edit: Thanks to amit this solution works for both negative and positive numbers:

    float powe(float x, int exp)
    {
            if (exp < 0)
                    return powe(1/x, -exp);
            if (exp == 0)
                    return 1;
            if (exp == 1)
                    return x;
            if (((int)exp)%2==0)
                    return powe(x*x, exp/2);
            else
                    return x*powe(x*x, (exp-1)/2);
    }

For fractional exponent we can do below (Spektre method):

  1. Suppose you have x^0.5 then you easily calculate square root by this method: start from 0 to x/2 and keep checking x^2 is equal to the result or not in binary search method.

  2. So, in case you have x^(1/3) you have to replace if mid*mid <= n to if mid*mid*mid <= n and you will get the cube root of x.Same thing applies for x^(1/4), x^(1/5) and so on. In the case of x^(2/5) we can do (x^(1/5))^2 and again reduce the problem of finding the 5th root of x.

  3. However by this time you would have realized that this method works only in the case when you can convert the root to 1/x format. So are we stuck if we can't convert? No, we can still go ahead as we have the will.

  4. Convert your floating point to fixed point and then calculate pow(a, b). Suppose the number is 0.6, converting this to (24, 8)floating point yields Floor(0.6*1<<8) = 153(10011001). As you know 153 represents the fractional part so in fixed point this (10011001) represent (2^-1, 0, 0, 2^-3, 2^-4, 0, 0, 2^7).So we can again calculate the pow(a, 0.6) by calculating the 2,3, 4 and 7 root of x in fixed point. After calculating we again need to get the result in floating point by dividing with 1<<8.

Code for above method can be found in the accepted answer.

There is also a log based method:

x^y = exp2(y*log2(x))

Community
  • 1
  • 1
newbie_old
  • 470
  • 4
  • 19
  • 1
    Why do you think it's wrong? The second code actually take care of the negative exponent issue because `x^n = (x^-1)^(-n)` - – amit Jun 20 '15 at 21:19
  • @amit: what is the terminating condition? I coded this solution and it runs in infinite loop for negative exponent. – newbie_old Jun 20 '15 at 21:36
  • 1
    The first code indeed doesn't take care of negative exponent is expected to behave like that. But the second one is correct, even though you said it "seems wrong". My question is: Why does the second code seems wrong to you? And I mention it works because `x^n = (x^-1)^(-n)` – amit Jun 20 '15 at 21:40
  • @amit: Can you please clarify which is "first code" and which is "second code" you are referring here? – newbie_old Jun 20 '15 at 21:47
  • In your question: the one above is "first code" and the one below is "2nd code" – amit Jun 20 '15 at 21:48
  • Your implementation is wrong, replace `if (x&1)` with `if (exp % 2)`. – FalconUA Jun 20 '15 at 21:58
  • 1
    @FalconUA: yes silly mistake i wanted to do exp&1 to check for odd number. – newbie_old Jun 20 '15 at 22:09
  • Please consider changing the title to state this is about negative exponents, and whether these shall be integral (while _base_ would be trivial). – greybeard Jun 21 '15 at 07:41
  • @newbie_old I just added [binary search sqrt without multiplication](http://stackoverflow.com/a/34657972/2521214) it might be interesting for you It is basicly `DWORD sqrt(const DWORD &x)` from my answer but without the use of multiplication which can be a big speed up on bignums in comparison to other methods – Spektre Jan 07 '16 at 15:18

1 Answers1

5

Integer examples are for 32 bit int arithmetics, DWORD is 32bit unsigned int

  1. floating pow(x,y)=x^y

    Is usually evaluated like this:

    so the fractional exponent can be evaluated: pow(x,y) = exp2(y*log2(x)). This can be done also on fixed point:

  2. integer pow(a,b)=a^b where a>=0 , b>=0

    This is easy (you already have that) done by squaring:

        DWORD powuu(DWORD a,DWORD b)
            {   
            int i,bits=32;
            DWORD d=1;
            for (i=0;i<bits;i++)
                {
                d*=d;
                if (DWORD(b&0x80000000)) d*=a;
                b<<=1;
                }
            return d;
            }
    
  3. integer pow(a,b)=a^b where b>=0

    Just add few ifs to handle the negative a

        int powiu(int a,DWORD b)
         {
         int sig=0,c;
         if ((a<0)&&(DWORD(b&1)) { sig=1; a=-a; } // negative output only if a<0 and b is odd
         c=powuu(a,b); if (sig) c=-c;
         return c;
         }
    
  4. integer pow(a,b)=a^b

    So if b<0 then it means 1/powiu(a,-b) As you can see the result is not integer at all so either ignore this case or return floating value or add a multiplier variable (so you can evaluate PI equations on pure Integer arithmetics). This is float result:

        float powfii(int a,int b)
         {
         if (b<0) return 1.0/float(powiu(a,-b));
         else return powiu(a,b);
         }
    
  5. integer pow(a,b)=a^b where b is fractional

    You can do something like this a^(1/bb) where bb is integer. In reality this is rooting so you can use binary search to evaluate:

    • a^(1/2) is square root(a)
    • a^(1/bb) is bb_root(a)

    so do a binary search for c from MSB to LSB and evaluate if pow(c,bb)<=a then leave the bit as is else clear it. This is sqrt example:

        int bits(DWORD p) // count how many bits is p
            {
            DWORD m=0x80000000; int b=32;
            for (;m;m>>=1,b--)
             if (p>=m) break;
            return b;
            }
    
        DWORD sqrt(const DWORD &x)
            {
            DWORD m,a;
            m=(bits(x)>>1);
            if (m) m=1<<m; else m=1;
            for (a=0;m;m>>=1) { a|=m; if (a*a>x) a^=m; }
            return a;
            }
    

    so now just change the if (a*a>x) with if (pow(a,bb)>x) where bb=1/b ... so b is fractional exponent you looking for and bb is integer. Also m is the number of bits of the result so change m=(bits(x)>>1); to m=(bits(x)/bb);

[edit1] fixed point sqrt example

//---------------------------------------------------------------------------
const int _fx32_fract=16;       // fractional bits count
const int _fx32_one  =1<<_fx32_fract;
DWORD fx32_mul(const DWORD &x,const DWORD &y)   // unsigned fixed point mul
    {
    DWORD a=x,b=y;              // asm has access only to local variables
    asm {                       // compute (a*b)>>_fx32_fract
        mov eax,a               // eax=a
        mov ebx,b               // ebx=b
        mul eax,ebx             // (edx,eax)=eax*ebx
        mov ebx,_fx32_one
        div ebx                 // eax=(edx,eax)>>_fx32_fract
        mov a,eax;
        }
    return a;
    }
DWORD fx32_sqrt(const DWORD &x) // unsigned fixed point sqrt
    {
    DWORD m,a;
    if (!x) return 0;
    m=bits(x);                  // integer bits
    if (m>_fx32_fract) m-=_fx32_fract; else m=0;
    m>>=1;                      // sqrt integer result is half of x integer bits
    m=_fx32_one<<m;             // MSB of result mask
    for (a=0;m;m>>=1)           // test bits from MSB to 0
        {
        a|=m;                   // bit set
        if (fx32_mul(a,a)>x)    // if result is too big
         a^=m;                  // bit clear
        }
    return a;
    }
//---------------------------------------------------------------------------

so this is unsigned fixed point. High 16 bits are integer and low 16 bits are fractional part.

  • this is fp -> fx conversion: DWORD(float(x)*float(_fx32_one))
  • this is fp <- fx conversion: float(DWORD(x))/float(_fx32_one))
  • fx32_mul(x,y) is x*y it uses assembler of 80386+ 32bit architecture (you can rewrite it to karatsuba or whatever else to be platform independent)
  • fx32_sqrt(x) is sqrt(x)

    In fixed point you should be aware of the fractional bit shift for multiplication: (a<<16)*(b<<16)=(a*b<<32) you need to shift back by >>16 to get result (a*b<<16). Also the result can overflow 32 bit therefore I use 64 bit result in assembly.

[edit2] 32bit signed fixed point pow C++ example

When you put all the previous steps together you should have something like this:

//---------------------------------------------------------------------------
//--- 32bit signed fixed point format (2os complement)
//---------------------------------------------------------------------------
// |MSB              LSB|
// |integer|.|fractional|
//---------------------------------------------------------------------------
const int _fx32_bits=32;                                // all bits count
const int _fx32_fract_bits=16;                          // fractional bits count
const int _fx32_integ_bits=_fx32_bits-_fx32_fract_bits; // integer bits count
//---------------------------------------------------------------------------
const int _fx32_one       =1<<_fx32_fract_bits;         // constant=1.0 (fixed point)
const float _fx32_onef    =_fx32_one;                   // constant=1.0 (floating point)
const int _fx32_fract_mask=_fx32_one-1;                 // fractional bits mask
const int _fx32_integ_mask=0xFFFFFFFF-_fx32_fract_mask; // integer bits mask
const int _fx32_sMSB_mask =1<<(_fx32_bits-1);           // max signed bit mask
const int _fx32_uMSB_mask =1<<(_fx32_bits-2);           // max unsigned bit mask
//---------------------------------------------------------------------------
float fx32_get(int   x) { return float(x)/_fx32_onef; }
int   fx32_set(float x) { return int(float(x*_fx32_onef)); }
//---------------------------------------------------------------------------
int fx32_mul(const int &x,const int &y) // x*y
    {
    int a=x,b=y;                // asm has access only to local variables
    asm {                       // compute (a*b)>>_fx32_fract
        mov eax,a
        mov ebx,b
        mul eax,ebx             // (edx,eax)=a*b
        mov ebx,_fx32_one
        div ebx                 // eax=(a*b)>>_fx32_fract
        mov a,eax;
        }
    return a;
    }
//---------------------------------------------------------------------------
int fx32_div(const int &x,const int &y) // x/y
    {
    int a=x,b=y;                // asm has access only to local variables
    asm {                       // compute (a*b)>>_fx32_fract
        mov eax,a
        mov ebx,_fx32_one
        mul eax,ebx             // (edx,eax)=a<<_fx32_fract
        mov ebx,b
        div ebx                 // eax=(a<<_fx32_fract)/b
        mov a,eax;
        }
    return a;
    }
//---------------------------------------------------------------------------
int fx32_abs_sqrt(int x)            // |x|^(0.5)
    {
    int m,a;
    if (!x) return 0;
    if (x<0) x=-x;
    m=bits(x);                  // integer bits
    for (a=x,m=0;a;a>>=1,m++);  // count all bits
    m-=_fx32_fract_bits;        // compute result integer bits (half of x integer bits)
    if (m<0) m=0; m>>=1;
    m=_fx32_one<<m;             // MSB of result mask
    for (a=0;m;m>>=1)           // test bits from MSB to 0
        {
        a|=m;                   // bit set
        if (fx32_mul(a,a)>x)    // if result is too big
         a^=m;                  // bit clear
        }
    return a;
    }
//---------------------------------------------------------------------------
int fx32_pow(int x,int y)       // x^y
    {
    // handle special cases
    if (!y) return _fx32_one;                           // x^0 = 1
    if (!x) return 0;                                   // 0^y = 0  if y!=0
    if (y==-_fx32_one) return fx32_div(_fx32_one,x);    // x^-1 = 1/x
    if (y==+_fx32_one) return x;                        // x^+1 = x
    int m,a,b,_y; int sx,sy;
    // handle the signs
    sx=0; if (x<0) { sx=1; x=-x; }
    sy=0; if (y<0) { sy=1; y=-y; }
    _y=y&_fx32_fract_mask;      // _y fractional part of exponent
     y=y&_fx32_integ_mask;      //  y integer part of exponent
    a=_fx32_one;                // ini result
    // powering by squaring x^y
    if (y)
        {
        for (m=_fx32_uMSB_mask;(m>_fx32_one)&&(m>y);m>>=1);     // find mask of highest bit of exponent
        for (;m>=_fx32_one;m>>=1)
            {
            a=fx32_mul(a,a);
            if (int(y&m)) a=fx32_mul(a,x);
            }
        }
    // powering by rooting x^_y
    if (_y)
        {
        for (b=x,m=_fx32_one>>1;m;m>>=1)                            // use only fractional part
            {
            b=fx32_abs_sqrt(b);
            if (int(_y&m)) a=fx32_mul(a,b);
            }
        }
    // handle signs
    if (sy) { if (a) a=fx32_div(_fx32_one,a); else a=0; /*Error*/ }     // underflow
    if (sx) { if (_y) a=0; /*Error*/ else if(int(y&_fx32_one)) a=-a; }  // negative number ^ non integer exponent, here could add test if 1/_y is integer instead
    return a;
    }
//---------------------------------------------------------------------------

I have tested it like this:

float a,b,c0,c1,d;
int x,y;
for (a=0.0,x=fx32_set(a);a<=10.0;a+=0.1,x=fx32_set(a))
 for (b=-2.5,y=fx32_set(b);b<=2.5;b+=0.1,y=fx32_set(b))
    {
    if (!x) continue; // math pow has problems with this
    if (!y) continue; // math pow has problems with this
    c0=pow(a,b);
    c1=fx32_get(fx32_pow(x,y));
    d=0.0;
    if (fabs(c1)<1e-3) d=c1-c0; else d=(c0/c1)-1.0;
    if (fabs(d)>0.1)
     d=d; // here add breakpoint to check inconsistencies with math pow
    }
  • a,b are floating point
  • x,y are closest fixed point representations of a,b
  • c0 is math pow result
  • c1 is fx32_pow result
  • d is difference

    hope did not forget something trivial but it seems like it works properly. Do not forget that fixed point has very limited precision so the results will differ a bit ...

P.S. Take a look at this:

Community
  • 1
  • 1
Spektre
  • 41,942
  • 8
  • 91
  • 312
  • 's: please explain more for fractions? – newbie_old Jun 21 '15 at 08:14
  • @newbie_old do you know how binary search works (for example for sqrt?) – Spektre Jun 21 '15 at 08:17
  • @newbie_old added code for sqrt binary search and hint what to change ... if it is not enough comment me and I will code something but not right now I need to go to work for few hours – Spektre Jun 21 '15 at 08:22
  • Spektre: yes start from 0 to x/2 and keep checking x^2 is equal to the number or not in binary search method – newbie_old Jun 21 '15 at 08:26
  • @newbie_old so instead of `x^2` you check for `x^bb` or `x^(1/b)` where `bb=1/b` so use the exponent which is integer. But you start with MSB bit and shift bit by bit so you check only bits(x)/bb times (binary search not linear naive one) – Spektre Jun 21 '15 at 08:28
  • I just had a glance at your answer but it was not clear if the exponent is .45, how will you do the method you described. Possible to add example for 2^.45? – newbie_old Jun 21 '15 at 08:48
  • @newbie_old on integers you can evaluate only fully integer roots this way. `1/0.45=2.2222222` this is not integer root! if you want to use any fractional exponent then you can not use only integers. Fractional exponents with integer roots are `0.5, 0.3333333, 0.25, 0.2, 0.1666666, 0.142857, ...` – Spektre Jun 21 '15 at 13:48
  • I understand your point. Basically in square root we find out the by multiplying twice in the fractional exponent case we first see if we can represent the fraction in this 1/x format or not. If we can then we multiply "x" number of times to see if we have fractional result or not. – newbie_old Jun 21 '15 at 23:46
  • I saw your other answers for pow and I liked the idea of implementing with exponent function along with log. My next task is to implement exponent in fixed point. I don't see anyone doing that. – newbie_old Jun 21 '15 at 23:49
  • @newbie_old 1. see the `fixed point bignum pow` link there is mine fixed point implementation for bignums so you can tweak it to your bit wide. 2. the `(1/x =integer)` has also another reason if you are on integers how you represent fraction? (multiply by constant like fixed point or use 1/x instead of x) – Spektre Jun 22 '15 at 07:14
  • @Spketre: so you mean if I need to find out pow(x, y) and y being fraction and not representable in 1/x format then I can multiply that with some constant same as in the fixed point format? Can you elaborate this method with an example? – newbie_old Jun 22 '15 at 16:14
  • @newbie_old there are two approaches one is use log2,exp2 equation on floats or fixed point the other is convert `y` to set of computable exponents. for example if `y=0.75` you can rewrite it like `y=0.75=0.5+0.25` so `x^0.75=(x^0.5)*(x^0.25)` when you realize that `y` is represented by binary fixed point number then each set bit of it represent computable exponent ... so you just multiply the sub power results and that is it (0.75dec is 0.11bin hence the 0.5 and 0.25 exponents). But this is float or fixed point not integer anymore.(so we drift off your topic a bit)... – Spektre Jun 22 '15 at 20:25
  • great so algorithm for fractions exponent calculation is:1. Get the binary fixed point representation of y. 2. For each set bit in the representation calculate the value i.e. If exponent is .11bin then we get square root for x and then 4th root of x and multiply together and get the answer. 3.we already know how to get square root and 4th root and 8th root and so on. 4. Only problem is now we need to have one more step to get binary fixed point representation for y. Thanks for this. – newbie_old Jun 22 '15 at 20:48
  • @newbie_old fixed point is easy for example if you have `float` value `0.123` then just multiply it by fraction part size of your `fixed point` format and that is it. So if you have 24.8 bit format then just multiply by `2^8=1<<8=256` so `floor(0.123f*256.0f)=31` so `31dec=0.00011111bin` is binary representation of your `0.123f` number when you convert it back to float the number is `0.1171875dec` not `0.123` because 8bit is not precise enough ...error is 0.0058125. so yo uneed to be careful how many bits you allocate for fractional part – Spektre Jun 23 '15 at 07:02
  • I didn't get you. You mean convert .123 in binary fixed point and then calculate pow(x, binary_fixed_point_no) and then convert it back to floating point? I tried this method with pen and paper and it didn't work. – newbie_old Jun 23 '15 at 12:30
  • @newbie_old LOL that is only conversion between float and fixed point (and back) number representations. You can not expect integer pow to handle fixed point numbers... you need to rewrite integer pow to handle all operations of fixed point format (operation `*,/` must be shift corrected and protected from overflow/underflow). compare the `fx32_sqrt` and `sqrt` functions they are the same but `fx32_sqrt` handles numbers as fixed point you need to do the same for your pow function if you want to switch it to fixed point format – Spektre Jun 23 '15 at 13:42
  • @newbie_old well from your comment I got the impression this is a bit too much for you so I added the 32bit signed fixed point pow example done with the powering+rooting approach – Spektre Jun 23 '15 at 16:29
  • great code!! I think you don't work in Linux and probably working in some DSP code, as your formatting of code is different from convention. No doubt your code is good but I think if you can change it a bit with nicer variable and writing code with pure c instead of assembly, I am sure more people can understand your code and possible more up votes. Anyway thanks for being generous with your knowledge. One more thing the the result of fx32_sqrt should be reconverted to floating point right by dividing with binary fixed point number right? – newbie_old Jun 23 '15 at 23:16
  • @newbie_old that only depends on what for you need the result `x`. if you need the float value then yes convert to float by `fx32_get(x)` if you want integer then `x/=_fx32_one` or signed bit shift right by `_fx32_fract_bits` if you need fixed point then leave the result as is. The same goes for input operands ... PS I code for PC (Win32+VCL) and AVR32 these days booth small and huge things so my coding is marked by that. The asm is just shortcut because I was too lazy to do the 64bit mul and div with pure C/C++ for this (it is just example how to put thing together). – Spektre Jun 24 '15 at 07:14
  • @newbie_old The votes depend mostly on the topic not code formatting. For example if you post something about database, or Python or any newer technology then you got a magnitude more views/votes here – Spektre Jun 24 '15 at 07:17
  • i edited the question to add details about fraction also. If you are free you can check the details as i basically summarized our conversation. – newbie_old Jun 28 '15 at 16:50
  • @newbie_old check this out [real domain pow based on complex domain math](https://stackoverflow.com/a/67089172/2521214) – Spektre Apr 14 '21 at 10:11