Optimize power function for fixed point numbers

S

suppamax

Hi everybody!

I'm writing a C program for a PIC18F microcontroller.

I need to calculate a power function, in which both base and exponent
are fixed point numbers (ex: 3.15^1.13).

Using pow() function is too expensive...

Is there another way to do that?


Thanks,



Max
 
K

Keith Thompson

suppamax said:
I'm writing a C program for a PIC18F microcontroller.

I need to calculate a power function, in which both base and exponent
are fixed point numbers (ex: 3.15^1.13).

Using pow() function is too expensive...

Is there another way to do that?

How are these fixed point numbers represented? Does your compiler
have special support for them? Standard C's only arithmetic types are
integer and floating-point.

If the exponent were always an integer, you could do it with repeated
multiplication; you could save a few multiplications with judicious
use of squaring. But with a non-integral exponent, you're going to
have to do something very similar to what the pow() function does.

I don't think you've given us enough information to help you. We need
a better idea of how the operands are represented, what values they
can have, how precise you need the result to be, and so forth.

It's possible that comp.programming might be a better place to ask;
the solution you're looking for is likely to be an algorithm rather
that something specific to C.
 
U

user923005

Hi everybody!

I'm writing a C program for a PIC18F microcontroller.

I need to calculate a power function, in which both base and exponent
are fixed point numbers (ex: 3.15^1.13).

Using pow() function is too expensive...

Is there another way to do that?

Can you tell us why you need the power function?
There may be a work-around (e.g. using Horner's rule to evaluate
polynomials instead of pow()).
 
S

suppamax

How are these fixed point numbers represented? Does your compiler
have special support for them? Standard C's only arithmetic types are
integer and floating-point.

If the exponent were always an integer, you could do it with repeated
multiplication; you could save a few multiplications with judicious
use of squaring. But with a non-integral exponent, you're going to
have to do something very similar to what the pow() function does.

I don't think you've given us enough information to help you. We need
a better idea of how the operands are represented, what values they
can have, how precise you need the result to be, and so forth.

It's possible that comp.programming might be a better place to ask;
the solution you're looking for is likely to be an algorithm rather
that something specific to C.

--
Keith Thompson (The_Other_Keith) <[email protected]>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"


Numbers always have 2 digits, and are represented as integers.
For example, if the correct value is 3.15, it will be represented as
315.



Max
 
S

suppamax

Can you tell us why you need the power function?
There may be a work-around (e.g. using Horner's rule to evaluate
polynomials instead of pow()).

The function I need to realize is something like

exp = 1.15;
result = 0;
while (...) {
[evaluate base: it will be, for example, 4.77]
result += pow(base, exp);
}



Max
 
R

Richard Heathfield

suppamax said:

Numbers always have 2 digits, and are represented as integers.
For example, if the correct value is 3.15, it will be represented as
315.

That's a little confusing. If numbers always have 2 digits, 315 is not a
number! Did you mean 3 digits?
 
U

user923005

Sorry...

2 decimal digits.

so 3.15 -> 315

What is the largest possible value in your system?
What is the smallest possible value in your system?
How much memory space do you have available?
 
P

Paul Hsieh

Hi everybody!

I'm writing a C program for a PIC18F microcontroller.

I need to calculate a power function, in which both base and exponent
are fixed point numbers (ex: 3.15^1.13).

Using pow() function is too expensive...

Is there another way to do that?

It doesn't seem obvious to me. I guess you would want a break down
like:

two_pow_fromIM ( y * two_log_toIM ( x ) );

The idea would be that two_log_toIM and two_pow_fromIM could be
implemented as a scaling (normalize to the range 1 <= x < 2) then
either a post or pre-shift along with a table look up if the
resolution was small enough (and possibly perform interpolations). To
_fromIM and _toIM reflect the fact you might like to convert it to a
temporarily higher resolution intermediate value, or range corrected
for the particular input values.

I am not aware of any really good approximations to log() or 2exp()
except for taylor series or rational function approximations, which
will end up doing no better than using pow() directly. This table
based stuff would obviously compromise accuracy/resolution.
 
U

user923005

It doesn't seem obvious to me.  I guess you would want a break down
like:

    two_pow_fromIM ( y * two_log_toIM ( x ) );

The idea would be that two_log_toIM and two_pow_fromIM could be
implemented as a scaling (normalize to the range 1 <= x < 2) then
either a post or pre-shift along with a table look up if the
resolution was small enough (and possibly perform interpolations).  To
_fromIM and _toIM reflect the fact you might like to convert it to a
temporarily higher resolution intermediate value, or range corrected
for the particular input values.

I am not aware of any really good approximations to log() or 2exp()
except for taylor series or rational function approximations, which
will end up doing no better than using pow() directly.  This table
based stuff would obviously compromise accuracy/resolution.

The logarithm can also be calculated effectively using the AGM.

Carlson's (1972) algorithm is described in Math. Comp. 26(118):
543--549.
It may be a good choice since very limited precision is needed, so I
suspect that only a few iterations would be required.
There is a thread on it from 2002 in and
the title of which is:
"Speaking of natural log and AGM... I've gone either blind or mad or
both."

Given an effective logarithm calculation, it can be inverted to create
exp() via something like this idea:

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

long double f( long double x, long double k )
{
return expl( x ) - k;
}

int main(int argc, char **argv)
{
long double x;
long double k;
long double lnx;
long double y;
char pszString[256] = "2";

if ( argc < 2 )
{
printf( "Enter a number to find exponential of:" );
fgets( pszString, sizeof( pszString ), stdin );
puts( pszString );
}
else
strncpy( pszString, argv[1], sizeof( pszString ) );

k = atof( pszString );
x = expl( k ) * 1.00001; /* form estimate correct to 5 digits */
lnx = logl(x);
printf("answer = %30.20LeL\nx = %30.20LeL\n", expl( k ), x - x*lnx
+ x*k );

y = lnx - k;
x -= 3. * x * ( 2 + 3 * y + 2 * y * y ) * y /
( 6 + 12 * y + 11 * y * y + 6 * y * y * y );
printf("answer = %30.20LeL\nx = %30.20LeL\n", expl( k ), x );
return 0;
}
 
U

user923005

It doesn't seem obvious to me.  I guess you would want a break down
like:
    two_pow_fromIM ( y * two_log_toIM ( x ) );
The idea would be that two_log_toIM and two_pow_fromIM could be
implemented as a scaling (normalize to the range 1 <= x < 2) then
either a post or pre-shift along with a table look up if the
resolution was small enough (and possibly perform interpolations).  To
_fromIM and _toIM reflect the fact you might like to convert it to a
temporarily higher resolution intermediate value, or range corrected
for the particular input values.
I am not aware of any really good approximations to log() or 2exp()
except for taylor series or rational function approximations, which
will end up doing no better than using pow() directly.  This table
based stuff would obviously compromise accuracy/resolution.

The logarithm can also be calculated effectively using the AGM.

Carlson's (1972) algorithm  is described in Math. Comp. 26(118):
543--549.
It may be a good choice since very limited precision is needed, so I
suspect that only a few iterations would be required.
There is a thread on it from 2002 in and
the title of which is:
"Speaking of natural log and AGM... I've gone either blind or mad or
both."

Given an effective logarithm calculation, it can be inverted to create
exp() via something like this idea:

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

long double f( long double x, long double  k )
{
   return expl( x ) - k;

}

int main(int argc, char **argv)
{
   long double x;
   long double k;
   long double lnx;
   long double y;
   char pszString[256] = "2";

   if ( argc < 2 )
   {
      printf( "Enter a number to find exponential of:" );
      fgets( pszString, sizeof( pszString ), stdin );
      puts( pszString );
   }
   else
      strncpy( pszString, argv[1], sizeof( pszString ) );

   k = atof( pszString );
   x = expl( k ) * 1.00001; /* form estimate correct to 5 digits */
   lnx = logl(x);
   printf("answer = %30.20LeL\nx = %30.20LeL\n", expl( k ), x - x*lnx
+ x*k );

   y = lnx - k;
   x -= 3. * x * ( 2 + 3 * y + 2 * y * y ) * y /
   ( 6 + 12 * y + 11 * y * y + 6 * y * y * y );
   printf("answer = %30.20LeL\nx = %30.20LeL\n", expl( k ), x );
   return 0;



}

Another possiblity is to use cubic splines. That's why I asked about
memory. It might be some super-limited thing where 1000 nodes for
spline storage is too much to ask. Also, the largest and smallest
values will give a clue about how many nodes would be needed for an
answer accurate to 2 decimal places. If the range is large, range
reduction can still be used, of course.
 
P

Paul Hsieh

The logarithm can also be calculated effectively using the AGM.
Carlson's (1972) algorithm  is described in Math. Comp. 26(118):
543--549.
It may be a good choice since very limited precision is needed, so I
suspect that only a few iterations would be required.
There is a thread on it from 2002 in and
the title of which is:
"Speaking of natural log and AGM... I've gone either blind or mad or
both."
Given an effective logarithm calculation, it can be inverted to create
exp() via something like this idea:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
long double f( long double x, long double  k )
{
   return expl( x ) - k;
}
int main(int argc, char **argv)
{
   long double x;
   long double k;
   long double lnx;
   long double y;
   char pszString[256] = "2";
   if ( argc < 2 )
   {
      printf( "Enter a number to find exponential of:" );
      fgets( pszString, sizeof( pszString ), stdin );
      puts( pszString );
   }
   else
      strncpy( pszString, argv[1], sizeof( pszString ) );
   k = atof( pszString );
   x = expl( k ) * 1.00001; /* form estimate correct to 5 digits */
   lnx = logl(x);
   printf("answer = %30.20LeL\nx = %30.20LeL\n", expl( k ), x - x*lnx + x*k );
   y = lnx - k;
   x -= 3. * x * ( 2 + 3 * y + 2 * y * y ) * y /
   ( 6 + 12 * y + 11 * y * y + 6 * y * y * y );
   printf("answer = %30.20LeL\nx = %30.20LeL\n", expl( k ), x );
   return 0;
}

Another possiblity is to use cubic splines.  That's why I asked about
memory.

I briefly looked at Carlson's Algorithm as you suggested, and it does
indeed look interesting. Its always good to have an algorithm whose
accuracy can simply be increased *incrementally* by running it through
more and more iterations. This means making good multi-precision
implementations become viable. Thanks for the pointer!

But in general using splines or other forms of interpolation provide
maybe a few more bits of accuracy at most. So its not that useful
except in improving initial guess before running it through
iterations, or in low-precision situations (which may be suitable for
the OP.)
 
U

user923005

Hi everybody!
I'm writing a C program for a PIC18F microcontroller.
I need to calculate a power function, in which both base and exponent
are fixed point numbers (ex: 3.15^1.13).
Using pow() function is too expensive...
Is there another way to do that?
It doesn't seem obvious to me.  I guess you would want a break down
like:
    two_pow_fromIM ( y * two_log_toIM ( x ) );
The idea would be that two_log_toIM and two_pow_fromIM could be
implemented as a scaling (normalize to the range 1 <= x < 2) then
either a post or pre-shift along with a table look up if the
resolution was small enough (and possibly perform interpolations).  To
_fromIM and _toIM reflect the fact you might like to convert it to a
temporarily higher resolution intermediate value, or range corrected
for the particular input values.
I am not aware of any really good approximations to log() or 2exp()
except for taylor series or rational function approximations, which
will end up doing no better than using pow() directly.  This table
based stuff would obviously compromise accuracy/resolution.
The logarithm can also be calculated effectively using the AGM.
Carlson's (1972) algorithm  is described in Math. Comp. 26(118):
543--549.
It may be a good choice since very limited precision is needed, so I
suspect that only a few iterations would be required.
There is a thread on it from 2002 in and
the title of which is:
"Speaking of natural log and AGM... I've gone either blind or mad or
both."
Given an effective logarithm calculation, it can be inverted to create
exp() via something like this idea:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
long double f( long double x, long double  k )
{
   return expl( x ) - k;
}
int main(int argc, char **argv)
{
   long double x;
   long double k;
   long double lnx;
   long double y;
   char pszString[256] = "2";
   if ( argc < 2 )
   {
      printf( "Enter a number to find exponential of:" );
      fgets( pszString, sizeof( pszString ), stdin );
      puts( pszString );
   }
   else
      strncpy( pszString, argv[1], sizeof( pszString ) );
   k = atof( pszString );
   x = expl( k ) * 1.00001; /* form estimate correct to 5 digits */
   lnx = logl(x);
   printf("answer = %30.20LeL\nx = %30.20LeL\n", expl( k ), x - x*lnx + x*k );
   y = lnx - k;
   x -= 3. * x * ( 2 + 3 * y + 2 * y * y ) * y /
   ( 6 + 12 * y + 11 * y * y + 6 * y * y * y );
   printf("answer = %30.20LeL\nx = %30.20LeL\n", expl( k ), x );
   return 0;
}
Another possiblity is to use cubic splines.  That's why I asked about
memory.

I briefly looked at Carlson's Algorithm as you suggested, and it does
indeed look interesting.  Its always good to have an algorithm whose
accuracy can simply be increased *incrementally* by running it through
more and more iterations.  This means making good multi-precision
implementations become viable.  Thanks for the pointer!

But in general using splines or other forms of interpolation provide
maybe a few more bits of accuracy at most.  So its not that useful
except in improving initial guess before running it through
iterations, or in low-precision situations (which may be suitable for
the OP.)

My notion on the spline (strictly for the OP) was to use a spline to
create an approximate exp() function. It looked from his examples as
though he only needs 2 digits of precision + some integral size (not
sure what), and so a spline could be used (potentially) but it may
also be necessary to do range reduction (which is why I asked about
the extreme values that are possible).

It's the same notion and the Numerical Recipes' using a spline to
approximate a functions (I modified xsplint to include exp()):

E:\nr\c\ansi\recipes>cl xsplint.c splint.c spline.c nrutil.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 14.00.50727.762
for 80x86
Copyright (C) Microsoft Corporation. All rights reserved.

xsplint.c
splint.c
spline.c
nrutil.c
Generating Code...
Microsoft (R) Incremental Linker Version 8.00.50727.762
Copyright (C) Microsoft Corporation. All rights reserved.

/out:xsplint.exe
xsplint.obj
splint.obj
spline.obj
nrutil.obj

E:\nr\c\ansi\recipes>xsplint

sine function from 0 to pi

x f(x) interpolation
0.157080 0.156434 0.156351
0.471239 0.453990 0.453981
0.785398 0.707107 0.707088
1.099557 0.891007 0.890984
1.413717 0.987688 0.987663
1.727876 0.987688 0.987663
2.042035 0.891007 0.890983
2.356194 0.707107 0.707089
2.670354 0.453991 0.453978
2.984513 0.156435 0.156433

***********************************
Press RETURN


exponential function from 0 to 1

x f(x) interpolation
0.050000 1.051271 1.051268
0.150000 1.161834 1.161834
0.250000 1.284025 1.284025
0.350000 1.419068 1.419067
0.450000 1.568312 1.568312
0.550000 1.733253 1.733253
0.650000 1.915541 1.915540
0.750000 2.117000 2.116999
0.850000 2.339647 2.339646
0.950000 2.585710 2.585709

***********************************
Press RETURN


The use of a cubic spline means that only 3 multiplies are needed to
estimate the exp() function.
 
D

David Thompson

How are these fixed point numbers represented? Does your compiler
have special support for them? Standard C's only arithmetic types are
integer and floating-point.

If the exponent were always an integer, you could do it with repeated
multiplication; you could save a few multiplications with judicious
use of squaring. But with a non-integral exponent, you're going to
have to do something very similar to what the pow() function does.
You _could_ take a 100th root once and then do an integer power -- or
an integer power and then a 100th root. But I don't know if this would
actually be cheap (enough) on the given platform, and you'd need
someone who actually knows numerical analysis (unlike me) to determine
how much error you would (or might) end up with.
I don't think you've given us enough information to help you. We need
a better idea of how the operands are represented, what values they
can have, how precise you need the result to be, and so forth.

It's possible that comp.programming might be a better place to ask;
the solution you're looking for is likely to be an algorithm rather
that something specific to C.

Good point. I should have read that first. <G>

- formerly david.thompson1 || achar(64) || worldnet.att.net
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top