Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Optimizing pow() function

160 views
Skip to first unread message

pozz...@gmail.com

unread,
Apr 22, 2013, 11:23:51 AM4/22/13
to
I have a simple (16-bit) embedded platform and I have to make the following calculations.

unsigned int
func(unsigned int adc, unsigned int pwr, unsigned int pt, double exp) {
return (unsigned int)(pow((double)adc, exp) *
(double)pwr / pow((double)pt, exp));
}

I noticed this calculation takes too much time, most probably for pow() function call and double conversion.

I'm finding a way to simplify/optimize this function. Consider that adc and pt parameters are in the range 0..1023, exp is in the range 1.00-3.00, and pwr can be 50-10000.

Any suggestion to avoid pow() function call?

Eric Sosman

unread,
Apr 22, 2013, 11:42:37 AM4/22/13
to
Simple enough to avoid half of them:

return pwr * pow( (double)adc / (double)pt, exp);

This is "algebraically equivalent" to the original, although
it probably won't give exactly the same result every time.

--
Eric Sosman
eso...@comcast-dot-net.invalid

Rich Webb

unread,
Apr 22, 2013, 2:08:24 PM4/22/13
to
You might try asking over in comp.arch.embedded, where this kind of an
issue is more often addressed than here in a general C group.

Depending on what you do with the result of func(), you might be able
to get away with operating on the logarithms, applying Jack Crenshaw's
bitlog() function as a fast approximation.

Fred K

unread,
Apr 22, 2013, 2:22:54 PM4/22/13
to
On Monday, April 22, 2013 11:08:24 AM UTC-7, Rich Webb wrote:
> On Mon, 22 Apr 2013 08:23:51 -0700 (PDT), pozz...@gmail.com wrote: >I have a simple (16-bit) embedded platform and I have to make the following calculations. > >unsigned int >func(unsigned int adc, unsigned int pwr, unsigned int pt, double exp) { > return (unsigned int)(pow((double)adc, exp) * > (double)pwr / pow((double)pt, exp)); >} > >I noticed this calculation takes too much time, most probably for pow() function call and double conversion. > >I'm finding a way to simplify/optimize this function. Consider that adc and pt parameters are in the range 0..1023, exp is in the range 1.00-3.00, and pwr can be 50-10000. > >Any suggestion to avoid pow() function call? You might try asking over in comp.arch.embedded, where this kind of an issue is more often addressed than here in a general C group. Depending on what you do with the result of func(), you might be able to get away with operating on the logarithms, applying Jack Crenshaw's bitlog() function as a fast approximation.

If pt is indeed in the range 0...1023, you have to handle the extreme case where pt=0 somehow, since the result is infinity, unless adc is also zero, in which case the result is indeterminant.
--
Fred K

Edward A. Falk

unread,
Apr 22, 2013, 2:34:20 PM4/22/13
to
In article <78c5c8dc-88b7-4c98...@googlegroups.com>,
Are you not worried about overflow?

Your embedded processor almost certainly does not have a floating-point
unit. This explains why your code is so slow.

I see all values are guaranteed to be integer values.

If you're not worried about overflow in the intermediate expressions,
or you can simplify your expression to reduce the risk, then consider
writing your own "ipow()" function.

--
-Ed Falk, fa...@despams.r.us.com
http://thespamdiaries.blogspot.com/

James Kuyper

unread,
Apr 22, 2013, 2:49:03 PM4/22/13
to
On 04/22/2013 02:34 PM, Edward A. Falk wrote:
> In article <78c5c8dc-88b7-4c98...@googlegroups.com>,
> <pozz...@gmail.com> wrote:
>> I have a simple (16-bit) embedded platform and I have to make the following calculations.
>>
>> unsigned int
>> func(unsigned int adc, unsigned int pwr, unsigned int pt, double exp) {
>> return (unsigned int)(pow((double)adc, exp) *
>> (double)pwr / pow((double)pt, exp));
>> }
>>
>> I noticed this calculation takes too much time, most probably for pow() function call
>> and double conversion.
>>
>> I'm finding a way to simplify/optimize this function. Consider that adc and pt
>> parameters are in the range 0..1023, exp is in the range 1.00-3.00, and pwr can be
>> 50-10000.
>
> Are you not worried about overflow?
>
> Your embedded processor almost certainly does not have a floating-point
> unit. This explains why your code is so slow.
>
> I see all values are guaranteed to be integer values.

The variable exp is double, and he didn't say that only integral values
of exp are allowed. If that is the case, he can speed up the code a lot
by using multiplications, rather than calls to pow().

Note: 'exp' is a bad name for a variable, given that it hides the
<math.h> function of the same name.

BartC

unread,
Apr 22, 2013, 3:09:31 PM4/22/13
to
<pozz...@gmail.com> wrote in message
news:78c5c8dc-88b7-4c98...@googlegroups.com...
Is there a version of pow() that uses float instead of double? That might be
a bit faster if floating point has to be done in software.

Can exp be anything from 1.00 to 3.00? How often will it be 1.0, 2.0, and
3.0? (These can be optimised.) Will it also only be specified to two
decimals? That suggests only 201 different values, although making use of a
200K lookup table indexed by adc/pt and 100*exp is probably not that
practical, and the results will be approximate.

Are the same adc/pt and exp combinations likely to recur frequently? It
might be possible to cache the results of the calculations (in a table a lot
smaller than 200K entries), but execution time might become non-linear. If
the combinations are predictable, then the table could be precalculated (it
all depends how this func() function is used).

--
Bartc

James Kuyper

unread,
Apr 22, 2013, 3:35:38 PM4/22/13
to
On 04/22/2013 03:09 PM, BartC wrote:
...
> Is there a version of pow() that uses float instead of double? ...

C99 introduced powf(), which takes float arguments and returns a float
value.

Malcolm McLean

unread,
Apr 22, 2013, 4:09:01 PM4/22/13
to
On Monday, April 22, 2013 8:09:31 PM UTC+1, Bart wrote:
> <pozz...@gmail.com> wrote in message
>
>
> Can exp be anything from 1.00 to 3.00? How often will it be 1.0, 2.0, and
> 3.0? (These can be optimised.) Will it also only be specified to two
> decimals? That suggests only 201 different values, although making use of a
> 200K lookup table indexed by adc/pt and 100*exp is probably not that
> practical, and the results will be approximate.
>

pow(x, y) = exp(y * log(x)) for positive values.

the expensive part is working out log x. But if you can look it up, you need
far fewer than 200k entries.

--
You can read about how to implement the math library in my book Basic Algorithms
http://www.malcolmmclean.site11.com/www

Keith Thompson

unread,
Apr 22, 2013, 5:24:29 PM4/22/13
to
But I wouldn't count on it being significantly faster than pow().

--
Keith Thompson (The_Other_Keith) ks...@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

BartC

unread,
Apr 22, 2013, 5:27:39 PM4/22/13
to


"Keith Thompson" <ks...@mib.org> wrote in message
news:lnsj2id...@nuthaus.mib.org...
> James Kuyper <james...@verizon.net> writes:
>> On 04/22/2013 03:09 PM, BartC wrote:
>> ...
>>> Is there a version of pow() that uses float instead of double? ...
>>
>> C99 introduced powf(), which takes float arguments and returns a float
>> value.
>
> But I wouldn't count on it being significantly faster than pow().

Not on a desktop PC with floating point hardware. But the OP is using a
'simple' 16-bit processor.

--
Bartc

glen herrmannsfeldt

unread,
Apr 22, 2013, 5:31:24 PM4/22/13
to
If only a small number of different exp values occur, a look-up
table would be faster than pow. If I want an int result, I try to do
the whole calculation in integer arithmetic, even if it needs some
shifts to get the binary point in the right place.

Routines for fixed point scaled exp, log, and I believe pow are
in Knuth's "Metafont: The Program."

With only 10 bit input, I presume you don't need 53 bits of double.
With a little work doing scaling, it can all be done in fixed point
arithmetic. (Except that pow comes in as double, but either generate
the appropriate scaled fixed point value or send it in that way.)

-- glen

-- glen

Keith Thompson

unread,
Apr 22, 2013, 6:44:37 PM4/22/13
to
I still wouldn't *count* on it being significantly faster. If it
matters, measure it.

glen herrmannsfeldt

unread,
Apr 22, 2013, 10:17:47 PM4/22/13
to
Keith Thompson <ks...@mib.org> wrote:
> James Kuyper <james...@verizon.net> writes:
>> On 04/22/2013 03:09 PM, BartC wrote:

>>> Is there a version of pow() that uses float instead of double? ...

>> C99 introduced powf(), which takes float arguments and
>> returns a float value.

> But I wouldn't count on it being significantly faster than pow().

In a software only implementation, one might hope that it is
faster, but yes I wouldn't be surprised if it wasn't. It should
use logf() and expf(), which again might not be faster.

-- glen

pozz

unread,
Apr 23, 2013, 1:24:28 AM4/23/13
to
I have to make another restriction. pt and adc parameters could be in
the range 0..1023, but I'm using pt=800.
At the moment I don't need other values for pt, but I'm interested to
generalize this calculation with different values for pt in a small
range around 800 (for example, 700-900).

pozz

unread,
Apr 23, 2013, 1:25:09 AM4/23/13
to
Sure, I have halfed the time in this way, but I'd like to reduce the
process time more, if possible.

pozz

unread,
Apr 23, 2013, 1:27:43 AM4/23/13
to
exp can be in the range 1.00 to 3.00 (it derives from an integer value
in the range 100-300 divided by 100).


> Note: 'exp' is a bad name for a variable, given that it hides the
> <math.h> function of the same name.
>

You are right, thank you for this observation.

pozz

unread,
Apr 23, 2013, 1:35:49 AM4/23/13
to
Il 22/04/2013 21:09, BartC ha scritto:
> <pozz...@gmail.com> wrote in message
> news:78c5c8dc-88b7-4c98...@googlegroups.com...
>> I have a simple (16-bit) embedded platform and I have to make the
>> following calculations.
>>
>> unsigned int
>> func(unsigned int adc, unsigned int pwr, unsigned int pt, double exp) {
>> return (unsigned int)(pow((double)adc, exp) *
>> (double)pwr / pow((double)pt, exp));
>> }
>>
>> I noticed this calculation takes too much time, most probably for pow()
>> function call and double conversion.
>>
>> I'm finding a way to simplify/optimize this function. Consider that adc
>> and pt parameters are in the range 0..1023, exp is in the range
>> 1.00-3.00,
>> and pwr can be 50-10000.
>>
>> Any suggestion to avoid pow() function call?
>
> Is there a version of pow() that uses float instead of double? That
> might be
> a bit faster if floating point has to be done in software.

I will check.


> Can exp be anything from 1.00 to 3.00?

Yes, it is a sort of "fine calibration".


> How often will it be 1.0, 2.0, and
> 3.0? (These can be optimised.)

The same occurence of 1.03 or 2.43. Anyway, after exp value is
calibrated on the field during first stages of installation/setup, it
remains always the same.


> Will it also only be specified to two
> decimals? That suggests only 201 different values, although making use of a
> 200K lookup table indexed by adc/pt and 100*exp is probably not that
> practical, and the results will be approximate.

I already thought on this approach, but the look-up tables will be too big.


> Are the same adc/pt and exp combinations likely to recur frequently?It
> might be possible to cache the results of the calculations (in a table a
> lot
> smaller than 200K entries), but execution time might become non-linear.

In "real-time" systems, I don't like to have tasks that takes a
"non-linear" time to execute.


> If
> the combinations are predictable, then the table could be precalculated
> (it all depends how this func() function is used).
>

The exp parameter is a calibration value that is defined during first
stages. After that, it will remain always the same value.
I can pre-calculate the look-up table for that particular value. I need
a table of adc/pt combinations length (1023 integers, considering a
fixed pt value), so 2046 bytes... I don't have this space in RAM :-(

Francois Grieu

unread,
Apr 23, 2013, 3:38:08 AM4/23/13
to
Pozz wrote:

> I have a simple (16-bit) embedded platform and I have to make
> the following calculations.

unsigned int
func(unsigned int adc, unsigned int pwr, unsigned int pt, double exp) {
return (unsigned int)(pow((double)adc, exp) *
(double)pwr / pow((double)pt, exp));
}

> adc and pt parameters are in the range 0..1023,
> exp is in the range 1.00-3.00, and
> pwr can be 50-10000.

> I have to make another restriction. pt and adc parameters could be
> in the range 0..1023, but I'm using pt=800.
> At the moment I don't need other values for pt, but I'm interested to
> generalize this calculation with different values
> for pt in a small range around 800 (for example, 700-900).


Could you state what in the context is constant, or could be subject to
one-time precomputation, and what is variable (like, I guess, ADC?)

Also: does your environment has powf, which is to float what pow
is to double? It might be much faster.


If pt is constant we can save a little with

#define pt 800

unsigned func(unsigned adc, unsigned pwr, double exp) {
return pwr * pow( (1./pt) * adc, exp );
}


Similarly, if pt is a runtime parameter that seldom vary, we can do

double ginvpt;

// call when pt changes
void reparameterize( unsigned pt ) {
ginvpt = 1./pt;
}

unsigned func(unsigned adc, unsigned pwr, double exp) {
return pwr * pow( ginvpt * adc, exp );
}


We would do significantly better, including getting rid of pow, and perhaps
of double or float, if exp is constant (or seldom vary); and avoid the
final multiplication, and I guess double altogether, if pwr is constant
(or seldom vary).


Francois Grieu

pozz...@gmail.com

unread,
Apr 23, 2013, 4:27:27 AM4/23/13
to
On Monday, April 22, 2013 5:23:51 PM UTC+2, pozz...@gmail.com wrote:
> Any suggestion to avoid pow() function call?

Ok, it's better to explain what I'm trying to do.

I have a 10-bit ADC that acquires a voltage signal and convert it to a decimal number 0..1023.

This signal measures a power (Watt). The relation between voltage and power is theorically quadratic:

P = k * x ^ 2

where k is a constant that depends on the system and x is the ADC result.

In order to calibrate k, I measure with an instrument the power at a nominal/reference value, for example I have Pr power measured ad Vr voltage that xr ADC points. So I can write:

P = Pr * (x / xr) ^ 2

Usually I make the reference measurement at xr=800pt.

The quadratic law above is simple to implement, but in practice I have to fine-tune this law, because at low power levels the voltage/power relation seems to deviate from the quadratic law (I don't know exactly why, maybe some non-linear behaviour of some components). So I changed the formula with:

P = Pr * (x / xr) ^ a

where a is in the range 1-3.

As you can see, after calibration/setup/installation/test steps I finally have three constants: Pr, xr and a. For simplicity xr could be considered constant at compile time (800pt). Pr and a is machine dependent.

So my problem is to calculate:

p = (x / 800) ^ a = exp(a * ln(x / 800))

I already tried to create a look-up table for ln(x) with x in the range 0.001 (about 1 / 800) to 1.3 (about 1023 / 800), leaving only the exp() function call. It works on desktop computer, but I couldn't find free space for the table (about 2600 bytes) in the embedded platform, both in RAM and in Flash.

The best would be to have a simple formula (polynomial function?) that approximate well enough the above equation at run-time, without too big tables.

BartC

unread,
Apr 23, 2013, 6:13:30 AM4/23/13
to


"glen herrmannsfeldt" <g...@ugcs.caltech.edu> wrote in message
news:kl4r0b$64f$1...@speranza.aioe.org...
For what reasons wouldn't it be faster to emulate only 32-bit floating point
arithmetic instead of 64-bits, on a 16-bit processor?

--
Bartc

David Brown

unread,
Apr 23, 2013, 6:39:23 AM4/23/13
to
It is not uncommon in embedded toolchains for the single-precision
functions to be implemented like this:

float powf(float x, float y) {
return (float) pow((double) x, (double) y);
}

(I've made the casts explicit to show what's happening.)

Thus powf takes as long as pow, plus 3 casts.


This is partly a matter of saving code space in libraries by avoiding
duplication, and partly a matter of saving development time and costs
for the toolchain vendor. Usually when an embedded developer uses a
function like pow() on a 16-bit micro they either know that this means
big and slow code and thus probably don't mind slightly bigger and
slower code, or they have no idea what they are doing and are going to
write crap code anyway.


Richard Damon

unread,
Apr 23, 2013, 8:35:29 AM4/23/13
to
Hearing your problem, my guess would be that the issue isn't that the
exponent is a little bit off, but that there is an offset to the voltage
measured. Thus rather than changing to

P = Pr * (x / xr ) ^ a

you might want to look at

P = k * (x + x0) ^ 2


James Kuyper

unread,
Apr 23, 2013, 8:49:21 AM4/23/13
to
On 04/23/2013 01:27 AM, pozz wrote:
> Il 22/04/2013 20:49, James Kuyper ha scritto:
>> On 04/22/2013 02:34 PM, Edward A. Falk wrote:
>>> In article <78c5c8dc-88b7-4c98...@googlegroups.com>,
>>> <pozz...@gmail.com> wrote:
>>>> I have a simple (16-bit) embedded platform and I have to make the following calculations.
>>>>
>>>> unsigned int
>>>> func(unsigned int adc, unsigned int pwr, unsigned int pt, double exp) {
>>>> return (unsigned int)(pow((double)adc, exp) *
>>>> (double)pwr / pow((double)pt, exp));
>>>> }
>>>>
>>>> I noticed this calculation takes too much time, most probably for pow() function call
>>>> and double conversion.
>>>>
>>>> I'm finding a way to simplify/optimize this function. Consider that adc and pt
>>>> parameters are in the range 0..1023, exp is in the range 1.00-3.00, and pwr can be
>>>> 50-10000.
>>>
>>> Are you not worried about overflow?
>>>
>>> Your embedded processor almost certainly does not have a floating-point
>>> unit. This explains why your code is so slow.
>>>
>>> I see all values are guaranteed to be integer values.
>>
>> The variable exp is double, and he didn't say that only integral values
>> of exp are allowed. If that is the case, he can speed up the code a lot
>> by using multiplications, rather than calls to pow().
>
> exp can be in the range 1.00 to 3.00 (it derives from an integer value
> in the range 100-300 divided by 100).

OK, then you can't use that speed-up. Your options for speeding up the
calculation inside func() are limited to pretty much the advice you've
already been given:

1. You need to handle the case of pt==0 separately.
2. Cut the time nearly in half, by using pwr*pow(pd/(double)adc, exp)
3. Produce a small and unreliable speed up by changing to use
exclusively float rather than double. That means that 'exp' should be
float, too. For best results, make sure that the calculation of 'exp'
outside the function is done in single precision: (float)integer/100.0F
(or equivalently, and faster on some systems: (float)integer*0.01F).
4. Pre-compute logf(adc/(float)pt) for all non-zero values of adc and
pt, and calculate pwr*expf(exp*precomputed_table[adc][pt]). This takes
up a fair amount of memory; it's only worthwhile if the function is
going to be evaluated frequently. Note that you must handle values of
zero for adc and pt separately.
5. More extreme: pre-compute pow(adc/pt, exp) for all values of adc, pt,
and the integer you divide by 100 to calculate exp. This uses a lot more
memory, but can save a lot of time if func() is called sufficiently
frequently.

If you need more of a speed-up than that, you'll have to consider the
reorganizing your code that calls func(), and not just func() itself.
It's got four arguments: is it frequently called for several different
values of some of the arguments, while the remaining arguments are
constant for many different consecutive calls to func()? Even if that's
not true of the way func() is called now, could you reorder the
calculations so that it becomes true?
If so, you can achieve substantial speed-ups by breaking the evaluation
of the expression into two parts, one part that depends only upon the
values that are frequently the same, and the other part that depends
upon the values that vary frequently. Whether or not this would help
significantly depends upon which arguments vary infrequently, and which
ones vary frequently. If 'exp' is one of the ones that varies
infrequently, it won't help you much at all. If the combination of adc
and pt varies infrequently, it could help a great deal.
--
James Kuyper

James Kuyper

unread,
Apr 23, 2013, 9:01:58 AM4/23/13
to
On 04/23/2013 01:35 AM, pozz wrote:
...
> The exp parameter is a calibration value that is defined during first
> stages. After that, it will remain always the same value.

That's unfortunate, because the exp is the argument that provides the
least opportunities for speeding things up by reason of being constant.

> I can pre-calculate the look-up table for that particular value. I need
> a table of adc/pt combinations length (1023 integers, considering a
> fixed pt value), so 2046 bytes... I don't have this space in RAM :-(

If pt can be considered fixed, and exp can be considered fixed, you
could pre-compute denominator = pow(pt,exp), and then calculate
pwr*pow(adc,exp)/denominator, but that's still a multiplication, a
pow(), and a division, exactly the same operation count as
pwr*pow(adc/pt, exp), so it doesn't give you any advantage.
--
James Kuyper

Eric Sosman

unread,
Apr 23, 2013, 9:08:55 AM4/23/13
to
Unless you can provide more information (like, "exp is
always an integer or half-integer" or "I only need six bits'
accuracy in the result"), I see no approach that's likely to
make a further dramatic difference. (Giant precomputed tables
seem inappropriate for "a simple embedded platform.")

Sometimes it's more fruitful to take a step back and look
at the overall problem: Not "How can I do *this* faster," but
"If I did things differently, could I replace *this* with
something else?" What problem are you trying to solve?

--
Eric Sosman
eso...@comcast-dot-net.invalid

James Kuyper

unread,
Apr 23, 2013, 9:19:56 AM4/23/13
to
On 04/23/2013 04:27 AM, pozz...@gmail.com wrote:
...
> Ok, it's better to explain what I'm trying to do.
>
> I have a 10-bit ADC that acquires a voltage signal and convert it to a decimal number 0..1023.
> This signal measures a power (Watt). The relation between voltage and power is theorically quadratic:
>
> P = k * x ^ 2
>
> where k is a constant that depends on the system and x is the ADC result.
>
> In order to calibrate k, I measure with an instrument the power at a nominal/reference value, for example I have Pr power measured ad Vr voltage that xr ADC points. So I can write:
>
> P = Pr * (x / xr) ^ 2
>
> Usually I make the reference measurement at xr=800pt.
>
> The quadratic law above is simple to implement, but in practice I have to fine-tune this law, because at low power levels the voltage/power relation seems to deviate from the quadratic law (I don't know exactly why, maybe some non-linear behaviour of some components). So I changed the formula with:
>
> P = Pr * (x / xr) ^ a

Without seeing your data, I can't be sure, but I think that it's
unlikely that the correct formula is of that form. If you don't have a
theory that tells you what the shape of a curve should be, and you're
trying to fit it empirically, choose the form of the curve to make your
job easier. A power law with a non-integer power makes things very
difficult.

...
> The best would be to have a simple formula (polynomial function?) that approximate well enough the above equation at run-time, without too big tables.

I agree.
--
James Kuyper

88888 Dihedral

unread,
Apr 23, 2013, 9:36:13 AM4/23/13
to
glen herrmannsfeldt於 2013年4月23日星期二UTC+8上午5時31分24秒寫道:
Well, the benefits of integer additions and multiplications
for very long integers in distributed systems by
the number theory transforms and residue number systems
were long long time ago.

But these were applied in the high priced military custom-built
systems only.

There are no ready and cheap to use MCUs with more than
16 cores now with the complete tool-chain .




pozz

unread,
Apr 23, 2013, 5:10:20 PM4/23/13
to
Il 23/04/2013 14:35, Richard Damon ha scritto:
>
> Hearing your problem, my guess would be that the issue isn't that the
> exponent is a little bit off, but that there is an offset to the voltage
> measured. Thus rather than changing to
>
> P = Pr * (x / xr ) ^ a
>
> you might want to look at
>
> P = k * (x + x0) ^ 2

It is nonsense when x=0, because P must be 0 in that case.


pozz

unread,
Apr 23, 2013, 5:20:47 PM4/23/13
to
Il 22/04/2013 22:09, Malcolm McLean ha scritto:
> On Monday, April 22, 2013 8:09:31 PM UTC+1, Bart wrote:
>> <pozz...@gmail.com> wrote in message
>>
>>
>> Can exp be anything from 1.00 to 3.00? How often will it be 1.0, 2.0, and
>> 3.0? (These can be optimised.) Will it also only be specified to two
>> decimals? That suggests only 201 different values, although making use of a
>> 200K lookup table indexed by adc/pt and 100*exp is probably not that
>> practical, and the results will be approximate.
>>
>
> pow(x, y) = exp(y * log(x)) for positive values.
>
> the expensive part is working out log x. But if you can look it up, you need
> far fewer than 200k entries.

I'm trying some series that approximate log() and exp() and it seems is
simpler to approximate log() and not exp().

Any suggestions?


pozz

unread,
Apr 23, 2013, 5:20:51 PM4/23/13
to
Il 23/04/2013 15:19, James Kuyper ha scritto:
> On 04/23/2013 04:27 AM, pozz...@gmail.com wrote:
> ...
>> Ok, it's better to explain what I'm trying to do.
>>
>> I have a 10-bit ADC that acquires a voltage signal and convert it to a decimal number 0..1023.
>> This signal measures a power (Watt). The relation between voltage and power is theorically quadratic:
>>
>> P = k * x ^ 2
>>
>> where k is a constant that depends on the system and x is the ADC result.
>>
>> In order to calibrate k, I measure with an instrument the power at a nominal/reference value, for example I have Pr power measured ad Vr voltage that xr ADC points. So I can write:
>>
>> P = Pr * (x / xr) ^ 2
>>
>> Usually I make the reference measurement at xr=800pt.
>>
>> The quadratic law above is simple to implement, but in practice I have to fine-tune this law, because at low power levels the voltage/power relation seems to deviate from the quadratic law (I don't know exactly why, maybe some non-linear behaviour of some components). So I changed the formula with:
>>
>> P = Pr * (x / xr) ^ a
>
> Without seeing your data, I can't be sure, but I think that it's
> unlikely that the correct formula is of that form. If you don't have a
> theory that tells you what the shape of a curve should be, and you're
> trying to fit it empirically, choose the form of the curve to make your
> job easier. A power law with a non-integer power makes things very
> difficult.

Ok, I could try adding a linear term to the original equation:

P = k * x ^ 2 + h * x

But now it's very difficult to calibrate and find k and h constant in
such a way the curve passes through the reference point (xr, Pr) and
another reference low-power point (xs, Ps).

With my exponential equation, the calibration is very simple. I move to
the reference point (xr=800) and measure Pr. The curve passes always
throug the reference point (xr, Pr) even changing the second constant a
(the exponent).
So I can move to a second reference point corresponding to a low power
level and calibrate the constant a, without worrying about the first
reference point.

>
> ...
>> The best would be to have a simple formula (polynomial function?) that approximate well enough the above equation at run-time, without too big tables.
>
> I agree.

I'll try to find two good series to calculate exp() and log().


pozz

unread,
Apr 23, 2013, 5:22:18 PM4/23/13
to
Please, take a look at another reply from me about a better explanation
about what I'm trying to do.

James Kuyper

unread,
Apr 23, 2013, 5:24:10 PM4/23/13
to
You're essentially claiming certainty that there is no voltage offset,
that x0 must be exactly 0. You've already indicated that the data
doesn't exactly match theory. How certain are you (and on what basis?),
that the failure of reality to match theory doesn't involve a voltage
offset?

glen herrmannsfeldt

unread,
Apr 23, 2013, 5:35:22 PM4/23/13
to
pozz <pozz...@gmail.com> wrote:
> Il 23/04/2013 15:08, Eric Sosman ha scritto:

(snip)
>> Unless you can provide more information (like, "exp is
>> always an integer or half-integer" or "I only need six bits'
>> accuracy in the result"), I see no approach that's likely to
>> make a further dramatic difference. (Giant precomputed tables
>> seem inappropriate for "a simple embedded platform.")
>
> Please, take a look at another reply from me about a better
> explanation about what I'm trying to do.

Well, what we still don't know is exactly, or even approximately,
how accurate the resutls need to be.

For a reasonable range of values, log and exp can be done in fixed
point, and likely good enough for you, but we don't know that.

The algoritms in "Metafont: The Program" do computation on fixed
point values with 15 bits before and 16 bits after the binary point.
They should run much faster than software emulation of floating
point, and maybe fast enough for you.

(Metafont was originally in Pascal. It is more often now run as
a machine translation to C of the Pascal source.)

-- glen

James Kuyper

unread,
Apr 23, 2013, 5:54:08 PM4/23/13
to
On 04/23/2013 05:20 PM, pozz wrote:
> Il 23/04/2013 15:19, James Kuyper ha scritto:
>> On 04/23/2013 04:27 AM, pozz...@gmail.com wrote:
...
>>> In order to calibrate k, I measure with an instrument the power at a nominal/reference value, for example I have Pr power measured ad Vr voltage that xr ADC points. So I can write:
>>>
>>> P = Pr * (x / xr) ^ 2
>>>
>>> Usually I make the reference measurement at xr=800pt.
>>>
>>> The quadratic law above is simple to implement, but in practice I have to fine-tune this law, because at low power levels the voltage/power relation seems to deviate from the quadratic law (I don't know exactly why, maybe some non-linear behaviour of some components). So I changed the formula with:
>>>
>>> P = Pr * (x / xr) ^ a
>>
>> Without seeing your data, I can't be sure, but I think that it's
>> unlikely that the correct formula is of that form. If you don't have a
>> theory that tells you what the shape of a curve should be, and you're
>> trying to fit it empirically, choose the form of the curve to make your
>> job easier. A power law with a non-integer power makes things very
>> difficult.
>
> Ok, I could try adding a linear term to the original equation:
>
> P = k * x ^ 2 + h * x

Depending upon what your data looks like, I'd recommend a cubic term,
and possibly a constant one as well. If you use a package to fit your
data to a polynomial, and one of the terms has a coefficient that is not
statistically significant, you can always drop it at that point, you
don't have to make the decision before determining the parameters of the
fitted model.

> But now it's very difficult to calibrate and find k and h constant in
> such a way the curve passes through the reference point (xr, Pr) and
> another reference low-power point (xs, Ps).
>
> With my exponential equation, the calibration is very simple. I move to
> the reference point (xr=800) and measure Pr. The curve passes always
> throug the reference point (xr, Pr) even changing the second constant a
> (the exponent).
> So I can move to a second reference point correPPsponding to a low power
> level and calibrate the constant a, without worrying about the first
> reference point.

Packages that allow you to fit a data set to a polynomial are widely
available, and you should use a lot more than just two points to
calibrate the parameters of your fitted model.

>>> The best would be to have a simple formula (polynomial function?) that approximate well enough the above equation at run-time, without too big tables.
>>
>> I agree.
>
> I'll try to find two good series to calculate exp() and log().

Taking that approach builds into your model the assumption that you've
got a power law: In the absence of a good reason to believe that the
real curve is a power law (and you haven't given any), it's simpler just
to fit your actual data to a polynomial. You've got tight constraints on
the execution time and the available space for evaluating your model;
you don't want to make inefficient use of your limited time and memory
by biasing your fit in favor of a power law for which you can give no
theoretical justification.

pozz

unread,
Apr 23, 2013, 6:12:51 PM4/23/13
to
It could be, but not at very low voltages. When I have zero power, I
have zero voltage (0 ADC points)... this is for sure.

Myabe the offset starts increasing with the power level or voltage, like
in the following equation:

P = k * (x + x0(x)) ^ 2

but it's difficult to understand what is x0(x). The only thing I can
say is that x0(0)=0.



Eric Sosman

unread,
Apr 23, 2013, 6:31:07 PM4/23/13
to
On 4/23/2013 4:27 AM, pozz...@gmail.com wrote:
> On Monday, April 22, 2013 5:23:51 PM UTC+2, pozz...@gmail.com wrote:
>> Any suggestion to avoid pow() function call?
>
> Ok, it's better to explain what I'm trying to do.
>
> I have a 10-bit ADC that acquires a voltage signal and convert it to a decimal number 0..1023.
>
> This signal measures a power (Watt). The relation between voltage and power is theorically quadratic:
>
> P = k * x ^ 2
>
> where k is a constant that depends on the system and x is the ADC result.
>
> In order to calibrate k, I measure with an instrument the power at a nominal/reference value, for example I have Pr power measured ad Vr voltage that xr ADC points. So I can write:
>
> P = Pr * (x / xr) ^ 2
>
> Usually I make the reference measurement at xr=800pt.
>
> The quadratic law above is simple to implement, but in practice I have to fine-tune this law, because at low power levels the voltage/power relation seems to deviate from the quadratic law (I don't know exactly why, maybe some non-linear behaviour of some components). So I changed the formula with:
>
> P = Pr * (x / xr) ^ a
>
> where a is in the range 1-3.

The first thing that occurs to me is to use a simpler
parameterization, something along the lines of

P = k(x) * x^2

... where k(x) is (most likely) an interpolation in a smallish
table. The variability of k seems a better match for your
suspicion "maybe some non-linear behavior" than fiddling around
with the exponent.

--
Eric Sosman
eso...@comcast-dot-net.invalid

James Kuyper

unread,
Apr 23, 2013, 6:58:05 PM4/23/13
to
On 04/23/2013 06:12 PM, pozz wrote:
> Il 23/04/2013 23:24, James Kuyper ha scritto:
>> On 04/23/2013 05:10 PM, pozz wrote:
>>> Il 23/04/2013 14:35, Richard Damon ha scritto:
>>> >
>>>> Hearing your problem, my guess would be that the issue isn't that the
>>>> exponent is a little bit off, but that there is an offset to the voltage
>>>> measured. Thus rather than changing to
>>>>
>>>> P = Pr * (x / xr ) ^ a
>>>>
>>>> you might want to look at
>>>>
>>>> P = k * (x + x0) ^ 2
>>>
>>> It is nonsense when x=0, because P must be 0 in that case.
>>
>> You're essentially claiming certainty that there is no voltage offset,
>> that x0 must be exactly 0. You've already indicated that the data
>> doesn't exactly match theory. How certain are you (and on what basis?),
>> that the failure of reality to match theory doesn't involve a voltage
>> offset?
>
> It could be, but not at very low voltages. When I have zero power, I
> have zero voltage (0 ADC points)... this is for sure.

That would make sense only if your method for measuring the voltage was
perfectly accurate, which is simply impossible. Counting of discrete
entities (such as people or houses or cars) can be done, in some
circumstances, with perfect accuracy, but any measurement that doesn't
boil down to counting discrete entities always has an error. Whenever
someone tells you that the error in their measurements is zero, it just
means that they don't properly understand how their measurements were
performed.

Any finite error in the calibration of the instrument used to measure
the voltage will cause x0 to have a non-zero value.

> Myabe the offset starts increasing with the power level or voltage, like
> in the following equation:
>
> P = k * (x + x0(x)) ^ 2
>
> but it's difficult to understand what is x0(x). The only thing I can
> say is that x0(0)=0.

That's the wrong way to think about it. If you're justified in your
certainty that P(0) = 0 (which still seems unlikely to me), then I agree
that x0(x) must also be 0. It then follows, if P(x) is sufficiently
smooth, that x0(x) will have a power series expansion with a constant
term that is zero. (x-x0(x))^2 will therefore also have a power series
expansion, also with a constant term of 0. Therefore, you're better off
just directly fitting P(x) to a power series, and if you insist, you can
tell the fitting program to force the constant to be 0. However, don't
be surprised if letting the constant term be non-zero improves the fit.

Richard Damon

unread,
Apr 23, 2013, 11:04:01 PM4/23/13
to
Only if you assume your A/D is exactly accurate, which it is almost
certainly isn't. If you can claim have 0 error in your readings then you
have probably spent too much on your analog processing section, and are
probably still wrong. There are always offset errors in your processing
that will shift your data some.

If your system is inherently a square law system, and you are seeing
non-square law results at low signal levels, first look at measurement
errors before assuming the system isn't really square law.

pozz

unread,
Apr 24, 2013, 3:22:52 AM4/24/13
to
On Apr 23, 11:35 pm, glen herrmannsfeldt <g...@ugcs.caltech.edu>
wrote:
>
> Well, what we still don't know is exactly, or even approximately,
> how accurate the resutls need to be.

The result shouldn't be very accurate. Consider that the result is
always
an integer, so I loose many significant digit.

For example, if Pr=1000W I'd like to have an integer power even with
5W steps.

pozz

unread,
Apr 24, 2013, 3:28:27 AM4/24/13
to
Yes, I can use a high-degree polynomial function to best fit the curve
I
measure, but it would be very difficult to calibrate on the field,
machine
by machine. I'd like to have a good fit curve with a few parameters
to
calibrate in a simple way.

The quadratic law (a second degree polynomial) comes from the theory.
My idea to use exponential function is to best-fit the real curve
starting
from the theory, so changing a little the exponent, without adding too
much
parameters difficult to calibrate.

pozz

unread,
Apr 24, 2013, 3:32:20 AM4/24/13
to
On Apr 23, 11:54 pm, James Kuyper <jameskuy...@verizon.net> wrote:
> On 04/23/2013 05:20 PM, pozz wrote:
>
>
>
>
>
>
>
>
>
> > Il 23/04/2013 15:19, James Kuyper ha scritto:
I can't use the powerful software on the field, machine by machine.


> >>> The best would be to have a simple formula (polynomial function?) that approximate well enough the above equation at run-time, without too big tables.
>
> >> I agree.
>
> > I'll try to find two good series to calculate exp() and log().
>
> Taking that approach builds into your model the assumption that you've
> got a power law: In the absence of a good reason to believe that the
> real curve is a power law (and you haven't given any), it's simpler just
> to fit your actual data to a polynomial. You've got tight constraints on
> the execution time and the available space for evaluating your model;
> you don't want to make inefficient use of your limited time and memory
> by biasing your fit in favor of a power law for which you can give no
> theoretical justification.

The theory says that the exponent should be 2 (P=V^2/R). In practice
I have
a shift from the theory, I don't know why (I'm not an hw expert).

So I need to adjust a law that depends on a small number of parameters
(in my
case, Pr and the exponent) on the field, machine by machine, with a
couple
of measures.

pozz

unread,
Apr 24, 2013, 3:38:59 AM4/24/13
to
On Apr 24, 12:31 am, Eric Sosman <esos...@comcast-dot-net.invalid>
wrote:
>
>      The first thing that occurs to me is to use a simpler
> parameterization, something along the lines of
>
>         P = k(x) * x^2
>
> ... where k(x) is (most likely) an interpolation in a smallish
> table.  The variability of k seems a better match for your
> suspicion "maybe some non-linear behavior" than fiddling around
> with the exponent.

How can I calibrate this "smallish table"? I would need to make many
measurements for each machine on the field.

If I write your function as

P = Pr * k(x) * x^2

where x is the ADC points normalized to 800 (reference value).

It should be nice if k(x) would be a function that values 1 when x=0
and x=1, and in between change smoothly, with the maximum at the
center
(x=0.5). This maximum correction at the center (about +/-30%) should
be the second parameter to calibrate in a simple way with a second
measurement, without changing the calibration of Pr (made during the
first measurement).

BartC

unread,
Apr 24, 2013, 5:39:11 AM4/24/13
to
<pozz...@gmail.com> wrote in message
news:6293f545-bb02-40d4...@googlegroups.com...
> On Monday, April 22, 2013 5:23:51 PM UTC+2, pozz...@gmail.com wrote:

> I have a 10-bit ADC that acquires a voltage signal and convert it to a
> decimal number 0..1023.

> The quadratic law above is simple to implement, but in practice I have to
> fine-tune this law, because at low power levels the voltage/power relation
> seems to deviate from the quadratic law (I don't know exactly why, maybe
> some non-linear behaviour of some components).

Low input impedance of the ADC? It would be interesting to find out why
that's happening, although fixing it in hardware would involve extra costs,
and it sounds like many of these units already exist!

However if I had to solve that in software, on a processor where 2KB of
memory is a big deal, then I would stay well away from double-precision
floating point arithmetic, especially involving powers, logarithms and
anti-logarithms. You've also said elsewhere that you only need accuracy of
0.5% (5W in 1000W), so integer/fixed-point methods could be viable.

--
Bartc

Bart van Ingen Schenau

unread,
Apr 24, 2013, 7:26:12 AM4/24/13
to
On Wed, 24 Apr 2013 00:32:20 -0700, pozz wrote:

> On Apr 23, 11:54 pm, James Kuyper <jameskuy...@verizon.net> wrote:
>>
>> Packages that allow you to fit a data set to a polynomial are widely
>> available, and you should use a lot more than just two points to
>> calibrate the parameters of your fitted model.
>
> I can't use the powerful software on the field, machine by machine.

Sure you can. Nobody is saying those methods should be integrated into
the machine.
You could enter the measurements into a curve-fitting program on a laptop
and just program the resulting coefficients into the machine.

>
> The theory says that the exponent should be 2 (P=V^2/R). In practice I
> have
> a shift from the theory, I don't know why (I'm not an hw expert).

If you don't know why you see the deviations, go talk with a HW engineer.
Preferably one that works on the same machine.
As for the deviations from the theory, are you sure that you are not
trying to do the calculations to a higher accuracy than your measurements
and hardware components allow?
For example, certain commonly used resistors can deviate from their
nominal value by as much as 10%. Those tolerances will make up a large
portion of the uncertainty in the R value of the formula above.
Also, the value from the ADC corresponds to a range of voltages, not just
a single voltage. This introduces a second factor of uncertainty into the
calculations.

Other possibilities for the apparent deviations from the theoretical
ideal are
- the ADC has a non-linear response curve (if you plot the voltage
against the ADC output, the steps are not all of the same size).
- The overall resistance in the system is not constant but varies with,
for example, the temperature. As components that consume power also heat
up as a side effect, this can have a significant effect if the system
consumes a lot of power.

>
> So I need to adjust a law that depends on a small number of parameters
> (in my
> case, Pr and the exponent) on the field, machine by machine, with a
> couple
> of measures.

My best advise would be to talk to some hardware engineers that know the
system to get a better understanding of the factors involved and how you
could compensate for them.
As it is now, you can't guarantee any better results for arbitrary
measurements than you would get with just blindly following the theory.

Bart v Ingen Schenau

James Kuyper

unread,
Apr 24, 2013, 8:31:39 AM4/24/13
to
Well, as you've found, modeling the curve by using a variable exponent
results in equations that you cannot evaluate quickly enough within your
memory constraints, so I'd recommend reconsidering that decision.

If you insist on "calibrating" your model with only a small number of
data points, it's really quite trivial to figure our the coefficients
for a polynomial that does so. Let v0, v1, v2 be three different
voltages, and P0, P1, P2 be the corresponding power levels. Then it's
quite straightforward to see that the following polynomial
necessarily goes through all three data points:

P(x) = P0*(x-x1)*(x-x2)/((x0-x1)*(x0-x2)) +
P1*(x-x0)*(x-x2)/((x1-x0)*(x1-x2)) +
P2*(x-x0)*(x-x1)/((x2-x0)*(x2-x1))

I can't take credit for this coming up with that formulation; but I also
can't give proper credit, because I can't remember the name of the
famous mathematician this approach is named for. It may have been Lagrange.

After filling in values for x0, x1, x2, P0, P1, P2, it's simple enough
to expand and simplify down to a simple quadratic expression that your
program can evaluate with reasonable efficiency and negligible storage
requirements.

Once you understand why it's "obvious" that this gives you a polynomial
that goes precisely through those three data points, it should be
trivial to figure out how to extend this to an arbitrarily large number
of points, but I wouldn't recommend going beyond 4.
--
James Kuyper

Tim Rentsch

unread,
Apr 24, 2013, 7:15:47 PM4/24/13
to
> and another reference low-power point (xs, Ps). [snip elaboration]

It has gradually become apparent that the problem you're having
is better thought of as a problem in numerical approximation
rather than just one of performance.

Expanding on a suggestion of Eric Sosman, let's make a guess
that we can tweak a V**2 relationship a little bit and that
will give us a good approximation. The very simplest tweak
is a linear function in v, so:

Power = (a * v + b) * v * v

(In this equation you can think of the resistance as being
unity, or as being "hidden" in the parameters 'a' and 'b'.
I don't think this part is hard and so I won't explain it
further.)

Given that we have two reference values, which I will call
r and s (corresponding to your xr and xs above), we have

Pr = (a * r + b) * r * r
Ps = (a * s + b) * s * s

or writing them somewhat more "mathematically"

Pr = a rrr + b rr
Ps = a sss + b ss

These equations can easily be solved for parameters 'a'
and 'b', giving

a = (Pr ss - Ps rr) / (rrss (r-s))
b = (Ps rrr - Pr sss) / (rrss (r-s))

where a and b are determined as part of the calibration
process, knowing the four references values r, s, Pr, and Ps.

Once the calibration is done, calculating power based on voltage
is very easy -- just three multiplications and one addition:

P = (a * v + b) * v * v

This formula is easy to test, and should be tested. If even
this simple formula isn't fast enough, then you have a bigger
problem, and you need to think about, eg, using rational fixed
point arithmetic. If it is fast enough, then the question is
does it give you enough accuracy? It might. But if it doesn't,
we can take the same idea and use a quadratic tweak rather
than a linear tweak:

Power = (a*v*v + b*v + c) * v * v

In this case we would need three reference values, call them x, y,
and z, and the corresponding powers, Px, Py, Pz. The parameters a,
b, and c again can be found using linear algebra, using the six
reference values as inputs -- the formulas are more complicated but
probably still simple enough so they could be used in calibration
code. The resulting formula above can be computed using only four
multiplications and two additions - very probably plenty fast
enough. And it seems pretty likely to give results that are
accurate enough over the desired range.

A more sophisticated analysis would try to minimize the error over
the range of interest, eg, using a least squares fit, but to start
with it's worth trying the simple formulas to see if they do the
job.

Richard Damon

unread,
Apr 25, 2013, 12:28:12 AM4/25/13
to
On 4/24/13 3:32 AM, pozz wrote:

> The theory says that the exponent should be 2 (P=V^2/R). In practice
> I have
> a shift from the theory, I don't know why (I'm not an hw expert).
>
> So I need to adjust a law that depends on a small number of parameters
> (in my
> case, Pr and the exponent) on the field, machine by machine, with a
> couple
> of measures.
>

The question is, Why do you think the proper improved model of reality
is changing the exponent, and not a offset error in the measurement of
V? (which likely more closely matches reality).

Some of the other suggestions would be to add terms corresponding to
possible non-linearities in the system compared to your model, but these
are probably a lot smaller that the offset error.

With two variables to figure out, I would suggest AT LEAST 3
measurements to calibrate to make sure that you measurements fit your
model well, over a wide range of values, one being for V small enough
that the offset is very significant, and another for V being large
enough that the offset is really insignificant, with the third (or more)
in between.

Noob

unread,
Apr 25, 2013, 5:41:10 AM4/25/13
to
pozzugno wrote:

> Ok, it's better to explain what I'm trying to do.
>
> I have a 10-bit ADC that acquires a voltage signal and convert it to a decimal number 0..1023.
>
> This signal measures a power (Watt). The relation between voltage and power is theorically quadratic:
>
> P = k * x ^ 2
>
> where k is a constant that depends on the system and x is the ADC result.

Waaaaay off-topic.
You'd get better answers to these questions in comp.arch.embedded
or even an electronics newsgroup (in sci.electronics.* maybe)

pozz

unread,
Apr 25, 2013, 9:49:29 AM4/25/13
to
Il 24/04/2013 14:31, James Kuyper ha scritto:

> Once you understand why it's "obvious" that this gives you a polynomial
> that goes precisely through those three data points, it should be
> trivial to figure out how to extend this to an arbitrarily large number
> of points, but I wouldn't recommend going beyond 4.

Thank you for your suggestion, I'll try to explore on this way.

Seebs

unread,
Apr 25, 2013, 7:47:48 PM4/25/13
to
On 2013-04-23, pozz <pozz...@gmail.com> wrote:
> It could be, but not at very low voltages. When I have zero power, I
> have zero voltage (0 ADC points)... this is for sure.
>
> Myabe the offset starts increasing with the power level or voltage, like
> in the following equation:
>
> P = k * (x + x0(x)) ^ 2
>
> but it's difficult to understand what is x0(x). The only thing I can
> say is that x0(0)=0.

It is not unheard of for things to behave almost as though some input were
max(x - C, 0) for some constant C, rather than an unmodified x. If nothing
else, you might find it rewarding to:

1. Assemble a bunch of measurements.
2. Put them in a spreadsheet.
3. Ask a computer to find a good matching function.

-s
--
Copyright 2013, all wrongs reversed. Peter Seebach / usenet...@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
I am not speaking for my employer, although they do rent some of my opinions.
0 new messages