x * x * x * x ) + (a5 * x * x * x * x * x) + (a6 * x * x * x * x * x * x) + (a7 * x * x * x * x * x * x * x) ;
formatting link
No prob; Here's the meat:
"Given a polynomial of degree n,
p(x) = anxn + an-1xn-1 + ... + a1x1 + a0
One might suspect that n+(n-1)+(n-2)+...+1 = n(n+1)/2 multiplications would be needed to evaluate p(x) for a given x. However Horner's Rule shows that it can be rewritten so that only n multiplications are needed:
x * x * x * x ) + (a5 * x * x * x * x * x) + (a6 * x * x * x * x * x * x) + (a7 * x * x * x * x * x * x * x) ;
Yeah, but doesn't C have an exponentiation operator? It's been so long since I've done any real programming, I've forgotten most of what I learned. Isn't there a construct like x**y or so? (which is shorthand to write, but still needs all those multiplies)...
On a sunny day (Tue, 21 Sep 2010 11:15:56 -0400) it happened Spehro Pefhany wrote in :
(a4 * x * x * x * x ) + (a5 * x * x * x * x * x) +
Well, really. Your example was not working, so anyways I decided to write the reverse of v2t, t2v (means voltage to temperature) for all thermocouples one is likely to use. In the same time as people are discussing when to multiply (pun intended), I have a nice working program using the NIST polinomials you pointed out. I tested some randopm values, and the range warnings, all seems OK. Note that this is a bit trickier then it may seem at first sight, as different polinomials are used for different temperature ranges, and also additional processing is needed, for example in typ K: if temp > 0, than E = sum(i=0 to n) c_i t^i + a0 exp(a1 (t - a2)^2) if temp 400.0) return 0;
// E = sum(i=0 to n) c_i t^i
*voltage = 0.0; if(temperature 1000.0) return 0;
// E = sum(i=0 to n) c_i t^i
*voltage = 0.0; if(temperature 1200.0) return 0;
// E = sum(i=0 to n) c_i t^i
*voltage = 0.0; if(temperature 1372.0) return 0;
// if > 0, than E = sum(i=0 to n) c_i t^i + a0 exp(a1 (t - a2)^2) // if 1768.00) return 0;
return 1; } /* end function temperature_to_voltage_type_S */
void print_usage() { fprintf(stderr, "Panteltje (c) t2v-0.1 thermocouple temperature (in °C) to voltage calculator\n"); fprintf(stderr, "Usage: t2v type[E|J|K|R|S|T] temperature\n"); fprintf(stderr, "Example:\n\ t2v T -270\n"); } /* end function print_usage */
int main(int argc, char **argv) { double temperature; double voltage;
if(argc != 3) { print_usage();
exit(1); }
/* get temperature */ temperature = atof(argv[2]);
/* get thermocouple type */ switch(argv[1][0]) { case 'E': if(! temperature_to_voltage_type_E(temperature, &voltage) ) { fprintf(stderr, "temperature out of range, aborting.\n");
exit(1); }
break; case 'J': if(! temperature_to_voltage_type_J(temperature, &voltage) ) { fprintf(stderr, "temperature out of range, aborting.\n");
exit(1); }
break; case 'K': if(! temperature_to_voltage_type_K(temperature, &voltage) ) { fprintf(stderr, "temperature out of range, aborting.\n");
exit(1); }
break; case 'R': if(! temperature_to_voltage_type_R(temperature, &voltage) ) { fprintf(stderr, "temperature out of range, aborting.\n");
exit(1); } break; case 'S': if(! temperature_to_voltage_type_S(temperature, &voltage) ) { fprintf(stderr, "temperature out of range, aborting.\n");
exit(1); } break; case 'T': if(! temperature_to_voltage_type_T(temperature, &voltage) ) { fprintf(stderr, "temperature out of range, aborting.\n");
exit(1); }
break; default: print_usage();
exit(1); break; } /* end switch thermocouple type */
/* display to user in C, K, and F */ fprintf(stdout, "voltage is %.6f V\n", voltage / 1000.0);
(a4 * x * x * x * x ) + (a5 * x * x * x * x * x) + (a6 * x * x * x * x * x * x)
(a7 * x * x * x * x * x * x * x) ;
The question was about _implementation_, no?
The answer is that no, it's a really, really bad way to implement a polynomial evaluation.
Whether pow(x,n) is better or worse than than x*x*..*x (for floating point x and positive integer n) is probably dependent on the value of n, as well as the compiler and the target.
(a4 * x * x * x * x ) + (a5 * x * x * x * x * x) + (a6 * x * x * x * x * x * x)
(a7 * x * x * x * x * x * x * x) ;
It's simple, but inferior to using Horner's rule when you look at total performance, which has n multiplies total vs. (O) n * log2(n), right?
Best regards, Spehro Pefhany
--
"it's the network..." "The Journey is the reward"
speff@interlog.com Info for manufacturers: http://www.trexon.com
Embedded software/hardware/analog Info for designers: http://www.speff.com
The polynomial is going to be used to correct a _thermocouple_. How often is that going to need to be evaluated? 10 times per second at most?
If it really matters, there are also representations more compact than Horner--you don't need to do N multiplies to get x**N, it's more like log(N) multiplies. There are even FFT tricks to evaluate polynomials in a compact way.
The issue with polynomials represented as sums of coefficients times powers of x**N, using Horner or any of the trickier algorithms, is that the basis set is very nearly linearly dependent. That is, X**N looks a whole lot like X**N-2. (There's a discussion of this in the Wikipedia article on the Hilbert matrix,
formatting link
That makes the evaluation of the coefficients horribly ill conditioned, which loses accuracy quite independently of execution speed, compiler details, and everything else.
Using Chebyshev polynomials makes the whole thing very well conditioned, because Chebyshevs are just cosines with a distorted horizontal axis, so they're all nicely orthogonal and everything.
If you don't know about Clenshaw's rule, it's worth looking it up, because it's both useful and pretty. Wikipedia isn't that useful because it's just the math--
formatting link
but there are lots of other places to learn about it.
Cheers
Phil Hobbs
--
Dr Philip C D Hobbs
Principal
ElectroOptical Innovations
55 Orchard Rd
Briarcliff Manor NY 10510
845-480-2058
email: hobbs (atsign) electrooptical (period) net
http://electrooptical.net
ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here.
All logos and trade names are the property of their respective owners.