Keil Logo

Calculation with static double has different results

Next Thread | Thread List | Previous Thread Start a Thread | Settings

Details Message
Author
Thomas Johansen Duerr IT Service GmbH
Posted
3-Jul-2018 08:23 GMT
Toolset
None
New! Calculation with static double has different results

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

Author
Andrew Neil
Posted
3-Jul-2018 09:20 GMT
Toolset
None
New! RE: I have a voltage (double) from a ADC

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?

Author
Thomas Johansen Duerr IT Service GmbH
Posted
3-Jul-2018 09:33 GMT
Toolset
None
New! RE: I have a voltage (double) from a ADC

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?

Thomas

Author
Andrew Neil
Posted
3-Jul-2018 10:31 GMT
Toolset
None
New! RE: I have a voltage (double) from a ADC

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 ?

Author
Thomas Johansen Duerr IT Service GmbH
Posted
3-Jul-2018 10:43 GMT
Toolset
None
New! RE: I have a voltage (double) from a ADC

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.

Thomas

Author
Andrew Neil
Posted
3-Jul-2018 11:02 GMT
Toolset
None
New! RE: If i feed the function with the same values again and again ...

... 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 ?

Author
Thomas Johansen Duerr IT Service GmbH
Posted
3-Jul-2018 11:50 GMT
Toolset
None
New! RE: If i feed the function with the same values again and again ...

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.

Thomas

Author
Andrew Neil
Posted
3-Jul-2018 12:00 GMT
Toolset
None
New! RE: with the segmented calculation and the full formula, I also get some differences

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.

Author
Thomas Johansen Duerr IT Service GmbH
Posted
3-Jul-2018 13:00 GMT
Toolset
None
New! RE: with the segmented calculation and the full formula, I also get some differences

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.

Author
Westonsupermare Pier
Posted
3-Jul-2018 11:38 GMT
Toolset
None
New! RE: Calculation with static double has different 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.

Author
LPC User
Posted
3-Jul-2018 15:55 GMT
Toolset
None
New! RE: Calculation with static double has different results

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.

Author
Andrew Neil
Posted
3-Jul-2018 16:07 GMT
Toolset
None
New! RE: Calculation with static double has different results

Is it reproducible in the Simulator?

Author
Hans-Bernhard Broeker
Posted
3-Jul-2018 19:29 GMT
Toolset
None
New! RE: Calculation with static double has different results

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?

Author
Andrew Neil
Posted
4-Jul-2018 07:54 GMT
Toolset
None
New! RE: "static" does not actually preclude changing

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

Author
Thomas Johansen Duerr IT Service GmbH
Posted
4-Jul-2018 08:13 GMT
Toolset
None
New! RE: "static" does not actually preclude changing

Hi,

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

dResult = V0[paramIndex] + ( (dTempT0)*(P1[paramIndex]+(dTempT0)*(P2[paramIndex]+(dTempT0)*(P3[paramIndex]+P4[paramIndex]*(dTempT0))))/(dDivider));

// 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;
}

Author
Andrew Neil
Posted
4-Jul-2018 10:50 GMT
Toolset
None
New! RE: "static" does not actually preclude changing

So you spotted the tags to get bold - but not the ones for posting source code?


http://www.danlhenry.com/caps/keil_code.png

Author
Hans-Bernhard Broeker
Posted
5-Jul-2018 07:46 GMT
Toolset
None
New! RE: "static" does not actually preclude changing

Yes, I mean not static as a keyword, static in not changed.

Just because it's "not changed" (i.e. explicitly written to by the code), doesn't necessarily mean it will "never change", as you claimed earlier. Stuff like buffer overflow or wild pointers does happen.

See test code below.

Can't, because there's nothing to be seen: not even declarations, much less actual definitions of any of those paramater arrays.

If you expect people to help you, it's up to you to enable them to do so. A self-contained, i.e. complete, test case is just about the minimum requirement there. As is attention to details like the proper tags for posting source code.

Author
Thomas Johansen Duerr IT Service GmbH
Posted
5-Jul-2018 08:05 GMT
Toolset
None
New! RE: "static" does not actually preclude changing

Hi,

Sorry about the missed tag.
The complete code is seen below (Hopefully with the correct tags)

// Defined calculation values
#define cjT0t 2.5000000E+1
#define cjV0t 9.9198279E-01
#define cjP1t 4.0716564E-02
#define cjP2t 7.1170297E-04
#define cjP3t 6.8782631E-07
#define cjP4t 4.3295061E-11
#define cjq1t 1.6458102E-02
#define cjq2t 0.0000000E+00

// array with data
double T0[4] = {0, 0, 0, cjT0t};
double V0[4] = {0, 0, 0, cjV0t};
double P1[4] = {0, 0, 0, cjP1t};
double P2[4] = {0, 0, 0, cjP2t};
double P3[4] = {0, 0, 0, cjP3t};
double P4[4] = {0, 0, 0, cjP4t};
double q1[4] = {0, 0, 0, cjq1t};
double q2[4] = {0, 0, 0, cjq2t};
double q3[4] = {0, 0, 0, cjq2t};


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
  dResult = V0[paramIndex] + ( (dTempT0)*(P1[paramIndex]+(dTempT0)*(P2[paramIndex]+
              (dTempT0)*(P3[paramIndex]+P4[paramIndex]*(dTempT0))))/(dDivider));

  // 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;
}


// Data for calculation 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;

// Main loop
int main(void)
{
 // Init stuff

  while(1)
  {
    if(nIndex >= nCount)
    {
       nIndex = 0;
    }

     double dCalculation = Calculation(fCJTemps[nIndex], 3);
     nIndex++;
 }
}


.

Author
Hans-Bernhard Broeker
Posted
5-Jul-2018 07:48 GMT
Toolset
None
New! RE: "static" does not actually preclude changing

Even 'const' doesn't (necessarily) guarantee that variables will be unchangeable; eg, by errant pointers, buffer overrun, stack overflow, etc ...

For a reasonably standard emedded system, it does.

Author
LPC User
Posted
5-Jul-2018 08:48 GMT
Toolset
None
New! RE: "static" does not actually preclude changing

To be honest, I've not spent the time trying to interpret your formula, but I've just tried pasting your code into a simple project on an LPC845 (identical core to the LPC824).

I get consistent results of:

Index:1 Val=1.105545
Index:2 Val=1.105545
Index:3 Val=1.105545
Index:4 Val=1.105545
Index:5 Val=1.105545
Index:6 Val=1.105545
Index:7 Val=1.105545
Index:8 Val=1.105545
Index:9 Val=1.106827
Index:10 Val=1.106827
Index:11 Val=1.106827
Index:12 Val=1.106827
Index:13 Val=1.106827
Index:14 Val=1.106827
Index:15 Val=1.106827
Index:16 Val=1.106827

I've tried running it multiple times in a loop, resetting the CPU, powering down/up and always get the same.

So, at the moment, my conclusion must be that you've got some sort of corruption occurring with your project configuration.

Author
Andrew Neil
Posted
5-Jul-2018 10:29 GMT
Toolset
None
New! RE: The complete code is seen below

Is that the "complete code" that was exhibiting the original problem, or just the demo?

If it's just the demo, do you still see the problem in it?

LPC User wrote: "I get consistent results"

Author
Thomas Johansen Duerr IT Service GmbH
Posted
5-Jul-2018 14:03 GMT
Toolset
None
New! RE: The complete code is seen below

Hi,

It was just part of the complete code. But I have stripped everything else away in my project, and run the code (as posted).

Here are my results

Value dResult dResult1 dResult2
Index 1 27.7812 1.105545438064 1.105545438064 1.105545438064
Index 2 27.7812 1.105545438064 1.105545438064 1.105545438064
Index 3 27.7812 1.105545438064 1.105545438064 1.105545438064
Index 4 27.7812 1.105545438064 1.105545438064 1.105545438064
Index 5 27.7812 1.105545438064 1.105545438064 1.105545438064
Index 6 27.7812 1.105545438064 1.105545438064 1.105545438064
Index 7 27.7812 1.105545438064 1.105545438064 1.105545438064
Index 8 27.7812 1.105545438064 1.105545438064 1.105545438064
Index 9 27.8125 1.10671996255 1.10671996255 1.109160524578
Index 10 27.8125 1.10671996255 1.10671996255 1.107294639882
Index 11 27.8125 1.10671996255 1.10671996255 1.107294641133
Index 12 27.8125 1.10671996255 1.10671996255 1.107294639882
Index 13 27.8125 1.10671996255 1.10671996255 1.107294641133
Index 14 27.8125 1.10671996255 1.10671996255 1.109160524578
Index 15 27.8125 1.10671996255 1.10671996255 1.109160524578
Index 16 27.8125 1.10671996255 1.10671996255 1.109160524578

The dResult2 is the value with no segmented formula. It is changing every time using 27.8125 as the stimuli value. Not with the other.
And is NOT the same values when I restart the program as posted earlier. Next time i run it, it can be different values.

What I also have noticed, is that the value calculated with 27.8125 is a bit of compared to my TI86 calculator. It must be 1.106827150.
The result posted by LPC User using the LPC845 gives the correct value?

Why?

My project can be downloaded here:
https://owncloud.agramkow.com/owncloud/s/GeG3B6pFMVVqMRr

Author
LPC User
Posted
5-Jul-2018 14:49 GMT
Toolset
None
New! RE: The complete code is seen below

The result posted by LPC User using the LPC845 gives the correct value?

Are you asking if my result is correct? I don't know. As I said before, I didn't try to understand the calculation.

The value shown is the dCalculation. I didn't report the intermediate values.

Author
Thomas Johansen Duerr IT Service GmbH
Posted
5-Jul-2018 14:58 GMT
Toolset
None
New! RE: The complete code is seen below

Yes, you result are the correct ones. My is a bit of.

But it seems to be related to setting the LPC824 into low power mode.

Author
Thomas Johansen Duerr IT Service GmbH
Posted
5-Jul-2018 14:55 GMT
Toolset
None
New! RE: The complete code is seen below

Hi again,

I tried removing more and more code and it seems related to setting the CPU to low power mode.

It is related to this:

cmdData[0] = 12;
cmdData[1] = PWR_LOW_CURRENT;
cmdData[2] = 12;
__disable_irq();
LPC_ROM_API->pPWRD->set_power(cmdData, &response);
__enable_irq();

Removing this does the trick. With both the changing value and the precision

Any idea why? Because I need the low power mode.

Thomas

Author
LPC User
Posted
5-Jul-2018 15:18 GMT
Toolset
None
New! RE: The complete code is seen below

You've only got a 256 byte stack. You're not far off that with your code and I don't know what the internal ROM routines use.

Have you tried increasing that stack size?

Author
Andrew Neil
Posted
5-Jul-2018 15:32 GMT
Toolset
None
New! RE: it seems related to setting the CPU to low power mode

That's probably something you need to take to NXP, then...

Next Thread | Thread List | Previous Thread Start a Thread | Settings

  Arm logo
Important information

This site uses cookies to store information on your computer. By continuing to use our site, you consent to our cookies.

Change Settings

Privacy Policy Update

Arm’s Privacy Policy has been updated. By continuing to use our site, you consent to Arm’s Privacy Policy. Please review our Privacy Policy to learn more about our collection, use and transfers
of your data.