Dear Forum,
I have experienced some strange behaviour using float/double values in a formula calculation.
Using the same static double values the result may differ each time I run the calculation?
I'm using a LPC824 (Cortex M0) with no FPU and KEIL-MDK. No MicroLIB and default compiler version 5
I'm aware of the usage of double/float precision issue. But this problem is when using the same values, the result is different each time I run a calculation.
I have a voltage (double) from a ADC, that is run through a formula, that uses 7 coefs (double).
The formula is this:
dResult = V0[paramIndex] + ( (dTemp-T0[paramIndex])*(P1[paramIndex]+(dTemp-T0[paramIndex])*(P2[paramIndex]+(dTemp-T0[paramIndex])*(P3[paramIndex]+P4[paramIndex]*(dTemp-T0[paramIndex]))))/( 1+(dTemp-T0[paramIndex])*(q1[paramIndex]+q2[paramIndex]*(dTemp-T0[paramIndex]))));
V0[], T0[], P1[], P2[], P3[], P4[], q1[] and q2[] contains static double values. So they do neve change.
dTemp is the double value (voltage) passed to the function containing the formula.
If I also keep the dTemp stable (fixed static value of 27.8125) the dResult is different each time I run the formula. If I use the value of 27.7812 it is the same value each time.
Why do I see this behavior? Why is the result not the same each time?
Thomas
So how do you know that is not changing?
By how much is the result changing?
If you break that massive calculation down into steps, can you see where the change is happening?
Thanks for the fast reply.
I monitor the calculation result with the debugger. Breakpoint in the function and add the dResult to the watch. Each time I run I have a new value in th watch using one of the fixed value. Not the case with the other value. But I can also see it if I send the result over a rs232 line to a terminal.
The result is about 1.10554543806. The difference is seen from the 2nd or 3th decimal. Not the same each time.
Yes, I have tried breaking down the formula in smaller pieces.
dTempT0 = dTemp-T0[paramIndex]; dq1q2Temp = q1[paramIndex]+q2[paramIndex]*(dTempT0); dMulti = dTempT0*dq1q2Temp; dDivider = 1.0+dMulti;
dResult = V0[paramIndex] + ( (dTempT0)*(P1[paramIndex]+(dTempT0)*(P2[paramIndex]+(dTempT0)*(P3[paramIndex]+P4[paramIndex]*(dTempT0))))/(dDivider));
It seems to happen at the dMulti value. (For the fixed value im using in my test). Adding the calculation "dTempT0*dq1q2Temp" the watch result in correct value. But I guess the watch is using the PC to calculate the resulting value?
But as long I don't know the cause, it could happen elsewhere with a different value of dTemp?
You didn't say how you know that the value from the ADC isn't changing ?
Have you tried doing the same calculation on a PC ?
Or using a different compiler on the Target ?
Does Keil distinguish single/double precision ?
Hi,
Sorry. I know that because I have made a special build, where I don't use values from the ADC reading, but from a array with fixed double values (double dCJTemps[] = {27.7812, 27.7812, 27.8125, 27.8125, 27.8125} --> Fed to the function doing the calculations)
No, have not made a PC application doing the same.
Keil do distinguish single/double precision. But regarding the precision. If i feed the function with the same values again and again I would expect the function to return the same value each time. Not a new one each time.
Not tried a new compiler yet.
... I would expect the function to return the same value each time.
Indeed.
Is it a repeatable set of values that you get?
ie, if you run it a few times, then do a full reset, then run again - do you get the same sequence of results ?
I would check at different optimization levels Check stack depth Check routines/math prior to starting up other assorted code/interrupts Check routines in stand-alone fashion Have a self-test that checks the 64-bit word for the correct/consistent value Check Flash wait states, and clocking
I'd expect ARM code to report consistent results for consistent inputs.
PC base floating point code might provide a different value based on optimization and the FPU carrying higher precision intermediate values.
Yes, they seems to be the same result sequence after a restart.
Also, if I compare values calculated with the segmented calculation and the full formula, I also get some differences:
Result with formula broken into pieces: 1.105545404646 Result using the complete formula: 1.105546324063
They are calculated in the same function at the same time, with the same data?
Also, compared to excel the value when the formula has been broken into pieces is the correct one.
I think that is to be expected?
Floating-point is prone to rounding errors as not all values can be exactly represented.
So doing the calculation in a different order could well give a slightly different result - as different roundings occur.
Yes, that makes sense.
But it does not explain why I do get different result with the same set of data when I run the calculation.
I have tried different compiler optimization settings without any luck.
Should I try keep values as float types? I know I lose precision, but I need consistency and not precision.
I've been using the LPC8xx for a while now and not come across anything like that.
If you make a really minimal project, I could try it for you.
Is it reproducible in the Simulator?
contains static double values. So they do neve change.
That conclusion would generally be wrong, since in C or C++ programs, "static" does not actually preclude changing. For that, they would have to be "const".
And what about paramIndex: is that the same, too? What's it there for, other than to actually make that formula yield different values, for different values of paramIndex?
I think he's probably just using the word in its general English sense - not meaning the C 'static' keyword ... ?
Even 'const' doesn't (necessarily) guarantee that variables will be unchangeable; eg, by errant pointers, buffer overrun, stack overflow, etc ...
I will try to make a minimal project to reproduce it. I will return with that
Yes, I mean not static as a keyword, static in not changed. See test code below. I just call the calculation function with data from an array of double.
dResult, dResult1 and dresult in the function below can have different values because of rounding in the double calculation. But also some of the broken down calculation (dCalc11, dCalc2 etc) can have different values with the same data
// Data for the test double fCJTemps[] = {27.7812, 27.7812, 27.7812, 27.7812, 27.7812, 27.7812, 27.7812, 27.7812, 27.8125, 27.8125, 27.8125, 27.8125, 27.8125, 27.8125, 27.8125, 27.8125}; int nCount = 16; int nIndex = 0;
// Called from main loop if(nIndex >= nCount) { nIndex = 0; }
dResult = Calculation(fCJTemps[nIndex], 3); nIndex++;
double Calculation(double dTemp, byte paramIndex) {
dTempT0 = 0.0;
dq1q2Temp = 0.0;
dMulti = 0.0;
dDivider = 0.0;
// Broken up formula for the divider
dTempT0 = dTemp-T0[paramIndex];
dq1q2Temp = q1[paramIndex]+q2[paramIndex]*(dTempT0);
dMulti = dTempT0*dq1q2Temp;
dDivider = 1.0+dMulti;
// Broken up formula
dCalc1 = P4[paramIndex]*(dTempT0);
dCalc2 = (P3[paramIndex]+ P4[paramIndex]*(dTempT0));
dCalc3 = (dTempT0)*((P3[paramIndex]+ P4[paramIndex]*(dTempT0)));
dCalc4 = P2[paramIndex]+(dTempT0)*((P3[paramIndex]+ P4[paramIndex]*(dTempT0)));
dCalc5 = (dTempT0)*(P2[paramIndex]+(dTempT0)*((P3[paramIndex]+ P4[paramIndex]* (dTempT0))));
dCalc6 = P1[paramIndex]+dCalc5;
dCalc7 = (dTempT0)*(P1[paramIndex]+dCalc5);
// Result with pre calculated divider
// Result with all formula part pre calculated
dResult1 = V0[paramIndex] + ( dCalc7/(dDivider));
// Result with formula directly calculated
dResult2 = V0[paramIndex] + ( (dTemp-T0[paramIndex])*(P1[paramIndex]+(dTemp-T0[paramIndex])*(P2[paramIndex]+(dTemp-T0[paramIndex])*(P3[paramIndex]+P4[paramIndex]*(dTemp-T0[paramIndex]))))/( 1+(dTemp-T0[paramIndex])*(q1[paramIndex]+q2[paramIndex]*(dTemp-T0[paramIndex]))));
// Return the result here
return dResult; }
So you spotted the tags to get bold - but not the ones for posting source code?
www.danlhenry.com/.../keil_code.png