|
|
View previous topic :: View next topic |
Author |
Message |
foxOnTheRun
Joined: 17 Apr 2010 Posts: 43
|
To Float or not to float? |
Posted: Fri Jul 01, 2011 3:50 am |
|
|
Assumption: in the code I always use RAW unsigned int values when working with sensors.
All right so, floats operations are really handy to display some neat data on the LCD, float is the format of values from sensors, floats give those juicy right comma placed numbers and it's a joy to waste countless days to fine tune them, filters, smooth, numeric tricks...
..but float operations are so slooooww and once inserted in the code, they simply make it's size/execution explode istantly!
So, I'm trying to avoid them at all costs.
In my code I had:
Code: | printf(lcd_putc,"Batt:%2.1fC ", (float)val_temp()/16.41153191); |
Where val_temp() is the output in unsigned int16 of a reading from a LM35 (already with an OPAMP to amplify it's excursion) - and that magic number 16.41153191 is the scale ration.
I tryied to "optimize" this thingh and come up with:
Code: | printf(lcd_putc,"Batt:%2.0wC ", (val_temp()*15)/246); |
Where, *15 is overflow safe (1023*15<65k) and /246 yelds a total resize of 16,4.
Precision is "quite" ok, I could fine tune it more: I could increase the *15 and choose a new division factor which could get more near the original value.
What if I want a decimal place? I would need to get from the division a x10 number, so I could put a . with:
Code: | printf(lcd_putc,"Batt:%3.1wC ", (val_temp()*150)/246); |
But then I should use an int32 at least, because val_temp()*150 will overflow :\
What do you suggest, is my approach accepltable, are there other clever ways to save space?
P.S. Saving space in the code area of the PIC is the first issue, speed is secondary. _________________ Listen, why don't you relax? Take a pill, bake a cake or go and read the encyclopedia. |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9243 Location: Greensville,Ontario
|
|
Posted: Fri Jul 01, 2011 5:42 am |
|
|
quick comment
Stick with integer math.
I've used thousands of the LM35/LM34s over the past 2 decades and no 2 are identical,same holds true for the gain of op amps, so really unless this is a 'one off' project you're just playing with numbers for the sake of numbers.No amount of 'clever math tricks' will get two boards to be the same.
Reality is that if the sensor reads 20*C that's fine to us humans. 19.99996 or 20.123456 is NOT important. |
|
|
SherpaDoug
Joined: 07 Sep 2003 Posts: 1640 Location: Cape Cod Mass USA
|
|
Posted: Fri Jul 01, 2011 6:22 am |
|
|
If val_temp is coming from an A/D reading your LM35 you probably aren't getting more than 12 bits, and in most applications 8 bits will do fine. Properly scaled 8 bits gives better than 0.5% resolution.
I have used floats for occasional debug. But I don't think I have ever used floats in actual final code shipped to a customer. _________________ The search for better is endless. Instead simply find very good and get the job done. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19538
|
|
Posted: Fri Jul 01, 2011 7:38 am |
|
|
and some more comments:
Float's are _not_ the 'format of values from sensors'. Numbers arriving from sensors will always be integers
Think for a moment about things like DVM's. _scaled integers_ are what they display.
Digital filters commonly use scaled integers as well.
Anything requiring repeatable precision avoids float's at all costs (look at financial calculations for example).
Now, yes, your approach is a start. However you need 'L' in the %w declaration.
Then remember that float on the PIC16/18, only handles at best 6.5 decimal digits. The last four digits in your constant in the original value were doing nothing. You hardware is unlikely to be doing better than 0.1% accuracy anyway, so 90% of the values being displayed by the float, are 'fallacious'...
Remember also division is always slower than multiplication (except for constant using a binary value - 2,4 etc.).
So start by reciprocating your value.
*0.609327, would immediately be several times faster.
Now remembering the comment about binary divisions, multiply by 256
So:
Code: |
printf(lcd_putc,"Batt:%3.1LwC ", ((int32()val_temp())*1560)/256);
|
Potentially four digits of accuracy (0.6093*), and probably about 10* faster.
Best Wishes |
|
|
foxOnTheRun
Joined: 17 Apr 2010 Posts: 43
|
|
Posted: Fri Jul 01, 2011 9:34 am |
|
|
temtronic wrote: | quik comment
Stick with integer math.
I've used thousands of the LM35/LM34s over the past 2 decades and no 2 are identical,same holds true for the gain of op amps, so really unless this is a 'one off' project you're just playing with numbers for the sake of numbers.No amount of 'clever math tricks' will get two boards to be the same.
Reality is that if the sensor reads 20*C that's fine to us humans. 19.99996 or 20.123456 is NOT important. |
Agree! the LM35 is rated +/-0.5C accuracy already. _________________ Listen, why don't you relax? Take a pill, bake a cake or go and read the encyclopedia. |
|
|
foxOnTheRun
Joined: 17 Apr 2010 Posts: 43
|
|
Posted: Fri Jul 01, 2011 9:36 am |
|
|
Ttelmah wrote: | and some more comments:
Float's are _not_ the 'format of values from sensors'. Numbers arriving from sensors will always be integers
Best Wishes |
Right, but I was viewing a sensor from end user: anything "measured" should be a number with a scale (speed, weight, lenghts, temp, etc..).
So, floats, or, a number with a meaningfull "point" is the objective ;) _________________ Listen, why don't you relax? Take a pill, bake a cake or go and read the encyclopedia. |
|
|
foxOnTheRun
Joined: 17 Apr 2010 Posts: 43
|
|
Posted: Fri Jul 01, 2011 10:01 am |
|
|
Ttelmah wrote: | and some more comments:
*0.609327, would immediately be several times faster.
Now remembering the comment about binary divisions, multiply by 256
So:
Code: |
printf(lcd_putc,"Batt:%3.1LwC ", ((int32()val_temp())*1560)/256);
|
Potentially four digits of accuracy (0.6093*), and probably about 10* faster.
Best Wishes |
You are Right!! try to divide always by a multiple of 64 :)
In the end I did this:
Code: | printf(lcd_putc,"Batt:%3.1wC ", (val_temp()*39)/64); |
That is giving a 3 places integer number which I display with one decimal place. No overflow, "accuracy" is ok/good; no int32 and I save another 3% in code size (using a PIC16F690).
Thanks for the usefull suggestions to all! _________________ Listen, why don't you relax? Take a pill, bake a cake or go and read the encyclopedia. |
|
|
foxOnTheRun
Joined: 17 Apr 2010 Posts: 43
|
|
Posted: Fri Jul 01, 2011 10:07 am |
|
|
SherpaDoug wrote: | If val_temp is coming from an A/D reading your LM35 you probably aren't getting more than 12 bits, and in most applications 8 bits will do fine. Properly scaled 8 bits gives better than 0.5% resolution.
|
Right! in fact, diving by 64 (see post above) is just doing the trick.
Anyway, it's just a home project, I'm building a sort of Lion (whatever the chemistry) battery charger, and I'm using an LM35 near the battery to sample the temperature, I just needed a meaningfull .1 temp. reading so I could "see" is the battery was going to overheat or not (visual inspection). _________________ Listen, why don't you relax? Take a pill, bake a cake or go and read the encyclopedia. |
|
|
SherpaDoug
Joined: 07 Sep 2003 Posts: 1640 Location: Cape Cod Mass USA
|
|
Posted: Fri Jul 01, 2011 10:11 am |
|
|
Consider the difference between floats and fixed point numbers. You can calculate integer millidegrees much faster than you can true floats, and use %w to put the decimal where it belongs for display. The idea is that for most real world values the decimal point really doesn't need to float at all. If a thermometer is going to display 6.5 degrees, or 65.4 degrees or 654.3 degrees, the decimal does not move. _________________ The search for better is endless. Instead simply find very good and get the job done. |
|
|
foxOnTheRun
Joined: 17 Apr 2010 Posts: 43
|
|
Posted: Fri Jul 01, 2011 3:40 pm |
|
|
SherpaDoug wrote: | Consider the difference between floats and fixed point numbers. You can calculate integer millidegrees much faster than you can true floats, and use %w to put the decimal where it belongs for display. The idea is that for most real world values the decimal point really doesn't need to float at all. If a thermometer is going to display 6.5 degrees, or 65.4 degrees or 654.3 degrees, the decimal does not move. |
Totally agree! _________________ Listen, why don't you relax? Take a pill, bake a cake or go and read the encyclopedia. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19538
|
|
Posted: Sat Jul 02, 2011 2:17 am |
|
|
Glad you have worked out a solution that suits.
It might be quite 'informative', to just run this part of the code in MPLAB simulator, and time it. The division by 64, should only take perhaps a dozen machine cycles now, and the multiplication perhaps a couple of hundred cycles. Also the actual output in printf, will also be faster. The %w format, is only having to divide by ten in integer, for each digit, versus the %f version is having to do a float division for each digit.
I'd be expecting something around 10* speed gain.
Best Wishes |
|
|
FvM
Joined: 27 Aug 2008 Posts: 2337 Location: Germany
|
|
Posted: Sat Jul 02, 2011 2:52 am |
|
|
Digital signal processing of sensor data partly requires complex arithmetic, e.g. applying higher order polynomials or exponential functions. Although everyhing can be done in integer fixed point with respective coding effort, these calculations are an obvious domain of float arithmetic.
If lower speed and slightly higher code size aren't an issue, you can expect faster project completion and less hidden faults utilizing float, if complex calculations are involved. |
|
|
foxOnTheRun
Joined: 17 Apr 2010 Posts: 43
|
|
Posted: Sat Jul 02, 2011 3:46 pm |
|
|
Ttelmah wrote: | Glad you have worked out a solution that suits.
It might be quite 'informative', to just run this part of the code in MPLAB simulator, and time it. The division by 64, should only take perhaps a dozen machine cycles now, and the multiplication perhaps a couple of hundred cycles. Also the actual output in printf, will also be faster. The %w format, is only having to divide by ten in integer, for each digit, versus the %f version is having to do a float division for each digit.
I'd be expecting something around 10* speed gain. :)
Best Wishes |
I'm trying to get a rough value about the code speed, I've opened a thread on the matter a couple of days before (or less, at the bottom the link to it).
To get the execution time of some instruction, I'm doing this way:
Code: | enable_interrupts(int_TIMER1);
leap=0;
set_timer1(0);
// Code to analyze
lcd_gotoxy(1,1);
printf(lcd_putc,"%Lu", (val_volt()*30)/61);
// ends here
v1=get_timer1();
disable_interrupts(int_TIMER1);
printf(lcd_putc,"\f%Lu, %u", v1,leap); |
If timer1 overflows, it just increments leap; TIMER1 is set as divide_by_1. So, basically, I'm making the PIC displays the increments made by timer1 - with a 8MHz clock, I get 0.5us resolution, nice and effective :)
(can you confirm to be correct too?)
http://www.ccsinfo.com/forum/viewtopic.php?t=45729 - this is the thread were I'll post the same code, to keep things in order, I'll keep posting about the general code speed issues there ;) _________________ Listen, why don't you relax? Take a pill, bake a cake or go and read the encyclopedia. |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|