View previous topic :: View next topic |
Author |
Message |
Bollo
Joined: 01 Dec 2010 Posts: 25
|
Floats are broken? |
Posted: Wed Sep 14, 2011 4:12 pm |
|
|
Hello I'm using CCS 4.120 currently and I have a very concerning problem. I have a calculation involving heavy use of floats. If I printf or sprintf the result of the calculation the result is correct and my system works. If I comment out the sprintf or printf the result is incorrect. Here is the calculation
Code: | current_speed=(ENC_DIF*5.82E-5)/(ENC_TIME_DIF*5E-7); //worksout speed is m/s |
If I do this
Code: | sprintf(FFS,"%f",current_speed); |
on the next line it works, comment it out and it doesn't. FFS is a local char array.
The ENC_DIF and ENC_TIME_DIF vars are both local signed int16 and current_speed is a local float. How can sprintf/ printf be having such a profound effect on a calculation?
To be honest I have had instances in the past where I've been convinced that using printf with a float has caused the data in the float var to change but normally I just put it down to me doing something silly.
The code is part of a DC motor controller, this part is working out speed using a magnetic encoder, the speed is passed onto a PID controller which adjusts the duty cycle according to the set desired speed.
When I say the calc is successful I mean the motor starts spinning at the desired speed when the printf or sprintf is present. Without it the motor just judders.
Any one had anything like this happen to them? |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed Sep 14, 2011 4:22 pm |
|
|
This is the 2nd page of a thread that discusses a floating point problem
that may be similar to yours. See the proposed solution at the end of
the thread. I didn't try it, but you could test it. See if it makes it work.
http://www.ccsinfo.com/forum/viewtopic.php?t=45529&start=15 |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Wed Sep 14, 2011 5:16 pm |
|
|
just 4 grins ;-))))))
I have a much older version than you - but have had the odd float trouble in my past as well.
if you leave out the printf but do this:
Code: |
current_speed= (float) ((ENC_DIF*5.82E-5)/(ENC_TIME_DIF*5E-7));
|
OR in the event that the named vars are NOT floats already ....
Code: |
current_speed=(((float)ENC_DIF)*5.82E-5)/(((float)ENC_TIME_DIF)*5E-7);
|
Do either of those explicit declarations help? |
|
|
Bollo
Joined: 01 Dec 2010 Posts: 25
|
|
Posted: Wed Sep 14, 2011 7:37 pm |
|
|
Thanks for the replies. I already tried explicitly casting to no avail. I will attempt initialising them to a non zero number tomorrow (GMT) and let you know how it goes. |
|
|
Bollo
Joined: 01 Dec 2010 Posts: 25
|
|
Posted: Thu Sep 15, 2011 7:04 am |
|
|
I initialised all my floats to non zero (1's in fact) and still no change, the only thing that will make it work is sprintf / printf. It looks like floats are broken I will attempt to remove as many as I can and replace them with integer calculations. |
|
|
Bollo
Joined: 01 Dec 2010 Posts: 25
|
|
Posted: Thu Sep 15, 2011 8:19 am |
|
|
Oh I take it back! The one explicit cast I didn't try was casting current_speed to a float (even though a handful of lines above I initialise the variable as a float)
Code: | int8 ENC_GET_SPEED(){
signed int16 ENC_TIME_DIF,ENC_DIF;
unsigned int16 ENC_TIME2,ENC_POS2;
float current_speed=1.111;
if (GLOBAL_ERROR==1){return 2;} //Needs to be at the top of every top level function
if (duty == 0){SPEED=0.0;}
else{
ENC_TIME2=ENC_TIME1; //record the old time ready for the the new
ENC_POS2=ENC_POS1;
ENC_POS1=get_enc_pos();
ENC_TIME1=get_timer3();
ENC_TIME_DIF = ENC_TIME1 - ENC_TIME2;
if (ENC_TIME_DIF<0){ENC_TIME_DIF=(65535-ENC_TIME2)+ENC_TIME1;} //deals with 1 single overflow
ENC_DIF = ENC_POS2-ENC_POS1;
// if (ENC_DIF<0){ENC_DIF=(1023-ENC_POS1)+ENC_POS2;} //deals with overflow
if (ENC_DIF<-5){ENC_DIF=(1023-ENC_POS1)+ENC_POS2;}
if (ENC_DIF<0){ENC_DIF=0;}
float current_speed=(ENC_DIF*5.82E-5)/(ENC_TIME_DIF*5E-7); //worksout speed is m/s
// printf("<%1.2f>,",current_speed);
delay_us(100);
SPEED=rollingav(current_speed);
}
} |
This also has the same effect as a printf / sprintf. The calculation now works. Very odd. |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Thu Sep 15, 2011 8:44 am |
|
|
thats a heck of a function your wrote .
&&
all to return the nearest signed 8 bits
THAT YOU NEVER ACTUALLY ASSIGN ANYWAY ?????
yes heck of a function
your blend of a float constant with
signed and unsigned int16s with god-knows-what from rollingav()
is awesome but i'm amazed the compiler would accept it all.
1- makes my head spin.
2- proves how really EXCELLENT CCS is at dealing with sloppy code
just my 2 cents |
|
|
Bollo
Joined: 01 Dec 2010 Posts: 25
|
|
Posted: Thu Sep 15, 2011 9:06 am |
|
|
Sorry I should explain more. The float constant is not really a constant, it was an experiment to see if initialising the float to non zero would help the problem (see PCM's post above). The returning of the int8 is nothing to do with the actual function of the code, it returns an error code. The rolling average function takes in the current speed and returns a rolling average of the current speed (with past speeds of course). The purpose of the function is to update global variable SPEED which is used in many of the other top level functions. Now I'm not brilliant at coding, but I'm struggling to see how it could be done better.
Here it is cleaned up from the experimenting.
Code: | int8 ENC_GET_SPEED(){
signed int16 ENC_TIME_DIF,ENC_DIF;
unsigned int16 ENC_TIME2,ENC_POS2;
float current_speed;
if (GLOBAL_ERROR){return 2;} //Needs to be at the top of every top level function
if (duty == 0){SPEED=0.0;}
else{
ENC_TIME2=ENC_TIME1; //record the old time ready for the the new
ENC_POS2=ENC_POS1; // record the old position ready for the new
ENC_POS1=get_enc_pos(); // get motor position from encoder
ENC_TIME1=get_timer3(); // get current time
ENC_TIME_DIF = ENC_TIME1 - ENC_TIME2; //calculate the difference in time
if (ENC_TIME_DIF<0){ENC_TIME_DIF=(65535-ENC_TIME2)+ENC_TIME1;} //deals with 1 single overflow
ENC_DIF = ENC_POS2-ENC_POS1; //calculate the difference in motor positions
if (ENC_DIF<0){ENC_DIF=(1023- ENC_POS1)+ENC_POS2;} //deals with overflow
float current_speed=(ENC_DIF*5.82E-5)/(ENC_TIME_DIF*5E-7); //works out speed is m/s
SPEED=rollingav(current_speed); //return the weighted rolling average of the speed
}
}
|
I don't think the code is as sloppy as you think!
Now please if you have any ideas on how to do this in a more efficient way please let me know. |
|
|
FvM
Joined: 27 Aug 2008 Posts: 2337 Location: Germany
|
|
Posted: Thu Sep 15, 2011 9:53 am |
|
|
Quote: | This is the 2nd page of a thread that discusses a floating point problem
that may be similar to yours. See the proposed solution at the end of
the thread. I didn't try it, but you could test it. See if it makes it work. |
I have no active PIC16 projects now, so I'm reviewing the discussion out of curiosity. If I understand the results right, the present compiler version has still bugs with float and you are trying to find a workaround? |
|
|
Bollo
Joined: 01 Dec 2010 Posts: 25
|
|
Posted: Thu Sep 15, 2011 10:04 am |
|
|
I'm using a PIC18F26K20, and yes there seems to be bugs with floats and/or printf and floats. I have not come across a consistent work around so far. |
|
|
FvM
Joined: 27 Aug 2008 Posts: 2337 Location: Germany
|
|
Posted: Thu Sep 15, 2011 2:46 pm |
|
|
Do you have a minimum complete example that reproduces the problem? The code snippets posted in this thread as well in the other quoted ones have been never complete and several posts saying "can't reproduce the issue".
It would be helpful, if the example can be run in the simulator without depending on ADC input voltage or similar things. |
|
|
|