Power by squaring for negative exponents
Asked Answered
H

1

6

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))

Hegelianism answered 20/6, 2015 at 21:16 Comment(10)
Why do you think it's wrong? The second code actually take care of the negative exponent issue because x^n = (x^-1)^(-n) -Forepleasure
@amit: what is the terminating condition? I coded this solution and it runs in infinite loop for negative exponent.Hegelianism
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)Forepleasure
@amit: Can you please clarify which is "first code" and which is "second code" you are referring here?Hegelianism
In your question: the one above is "first code" and the one below is "2nd code"Forepleasure
Your implementation is wrong, replace if (x&1) with if (exp % 2).Hexagonal
@FalconUA: yes silly mistake i wanted to do exp&1 to check for odd number.Hegelianism
Please consider changing the title to state this is about negative exponents, and whether these shall be integral (while base would be trivial).Carbamidine
@Hegelianism I just added binary search sqrt without multiplication 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 methodsFoulmouthed
int exp is not quite a good choice for variable name because it conflicts with the standard exp() functionPanpsychist
F
6

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

  1. Floating pow(x,y)=xy

    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)=ab 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)=ab 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)=ab

    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)=ab 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

I 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:

Foulmouthed answered 21/6, 2015 at 8:11 Comment(22)
's: please explain more for fractions?Hegelianism
@Hegelianism do you know how binary search works (for example for sqrt?)Foulmouthed
@Hegelianism 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 hoursFoulmouthed
Spektre: yes start from 0 to x/2 and keep checking x^2 is equal to the number or not in binary search methodHegelianism
@Hegelianism 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)Foulmouthed
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?Hegelianism
@Hegelianism 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, ...Foulmouthed
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.Hegelianism
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.Hegelianism
@Hegelianism 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)Foulmouthed
@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?Hegelianism
@Hegelianism 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)...Foulmouthed
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.Hegelianism
@Hegelianism 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 partFoulmouthed
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.Hegelianism
@Hegelianism 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 formatFoulmouthed
@Hegelianism 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 approachFoulmouthed
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?Hegelianism
@Hegelianism 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).Foulmouthed
@Hegelianism 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 hereFoulmouthed
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.Hegelianism
@Hegelianism check this out real domain pow based on complex domain mathFoulmouthed

© 2022 - 2024 — McMap. All rights reserved.