![](templates/subSilver/images/CCSLogo.jpg) |
![CCS C Software and Maintenance Offers](templates/subSilver/images/forumAd6.jpg) |
View previous topic :: View next topic |
Author |
Message |
gjs_rsdi
Joined: 06 Feb 2006 Posts: 468 Location: Bali
|
Math question |
Posted: Fri Mar 24, 2017 6:56 pm |
|
|
CCS PCH C Compiler, Version 5.059, 31220
PIC18F26K22
Code: | #use delay(internal=64MHz) |
As I would like to have any math operation in less than 1us, I made the below test and got the out-commented times.
Code: | int a=250;
int b=2;
long c=250;
a=250;//0.0625us
a=a/2;//0.125us
a=250;//0.125us
a=a/b;//6.25us
c=250;//0.25us
c=c/2;//0.1875us
c=250;//0.125us
c=c/b;//21us
delay_cycles(1); |
Tried also with #include "math.h", same results, even worse.
How can be so big differences while dividing by 2 and dividing by an 8 bit int?
Also how can be that moving a literal to a long in one case takes 0.25us and other case 0.125us?
Best wishes
Joe |
|
![](templates/subSilver/images/spacer.gif) |
newguy
Joined: 24 Jun 2004 Posts: 1903
|
|
Posted: Fri Mar 24, 2017 7:10 pm |
|
|
Dividing by two is the same as doing a binary shift right by 1. Binary shifts are fast. Actual division is slow. There is no way around the long divide times.
For fun, repeat your exercise using floats. |
|
![](templates/subSilver/images/spacer.gif) |
gjs_rsdi
Joined: 06 Feb 2006 Posts: 468 Location: Bali
|
|
Posted: Fri Mar 24, 2017 7:34 pm |
|
|
Thanks for the reply Newguy.
Started with floats, as my program was using floats.
I am getting in the range of 70us.
Changed to *;/;+;-;. Not perfect, but acceptable.
One more question:
I am using floats in an other part of the program that it will be much more difficult to replace with simple math.
If an interrupt occur while performing a so long calculation it will be served immediately or just after the calculation is finished?
Best wishes
Joe |
|
![](templates/subSilver/images/spacer.gif) |
temtronic
Joined: 01 Jul 2010 Posts: 9168 Location: Greensville,Ontario
|
|
Posted: Sat Mar 25, 2017 5:35 am |
|
|
Yes, 'floats are fun' with a PIC ! Unless absolutely necessary, avoid them, use scaled integers...binary divisors(2,4,8,32), etc.
Re: Interrupts. Generally speaking PICs will complete the current machine code instruction they are executing (not an equation !), 'service' the interrupt, then return to where it left off and carry on. CCS adds code to make the operation 'tidy'.
Depending on your level of expertise and knowledge of PICs, stack, variables and what you need it to do, you can modify the CCS ISR or cut your own. For time sensitve code, this can help BUT change stuff down the road, and it might crash on you.....
Jay |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19358
|
|
Posted: Sat Mar 25, 2017 5:51 am |
|
|
Immediately, unless for some reason interrupts are disabled.
This is where 'scaled integers' come in very useful.
Have a look in the manual 'common questions and answers'. 'How much time do maths operations take'.
For instance, suppose I want to divide by three.
Assume an int8 value, stored in an int16.
Now
value/3 will take about 20uSec.
(value*85), and then just read the high byte of the result, will take about 2uSec!...
Similarly if I want to print a number from an ADC reading as 0.00 to 5.00v. Assume reading is 10bit (0 to 1023).
Each step of the ADC is 0.0048828v.
Now three different solutions:
First just take the ADC reading, change to float, multiply by 5.0, divide by 1024.0, and print as a float. Slowest solution. The compiler will presolve partially but may well still divide by 204.8. This will take about 110uSec, then it'll have to do floating point divisions by 10.0 for each digit to output. So for three actual digits, perhaps 440uSec.
Second just use *0.0048828. Potentially a little faster. Multiplication is typically about 4* faster than division, but it still has to use /10.0 for each digit. So perhaps 350uSec.
Third use integers:
Take ADC_reading. Put into an int32. Multiply by 32000. Now just read the high 16bits of this (so effectively /65536, but involving no maths at all). Total time to do this, about 16uSec. Now print this 16bit value using %4.2LW. Uses 16bit integer divisions. About 76uSec total. |
|
![](templates/subSilver/images/spacer.gif) |
gjs_rsdi
Joined: 06 Feb 2006 Posts: 468 Location: Bali
|
|
Posted: Sun Mar 26, 2017 1:21 pm |
|
|
Thank you for the answer Ttelmah and Jay
When testing in MPLAB simulator during long calculations the cursor jumping to the controller header file.
Can't see what is doing there but if an interrupt will occur during this time and it will be served immediately I can afford that.
Best wishes
Joe |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19358
|
|
Posted: Sun Mar 26, 2017 1:24 pm |
|
|
If you remark out the entry '#NOLIST' that is at the start of the include file for the processor, then re-compile, the code for the maths routines will be included, so you can see how this is done. |
|
![](templates/subSilver/images/spacer.gif) |
gjs_rsdi
Joined: 06 Feb 2006 Posts: 468 Location: Bali
|
|
Posted: Sun Mar 26, 2017 3:36 pm |
|
|
Thanks Ttelmah
I see the math, very complicated.
I am trying to rewrite the functions not to use float and break down the calculations to keep all below 1us.
Best wishes
Joe |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19358
|
|
Posted: Mon Mar 27, 2017 2:24 am |
|
|
1uSec. Probably not going to happen...
Even at 64MHz, that is only 16 machine instructions. Even if you solved everything just using (for example) a lookup table, just loading the values, and reading the result would take longer than this...
Aim for something like 10uSec, and with ingenuity, this may well be doable.
Division is always the slowest basic maths operation. Your chip has hardware to do an integer multiply, but nothing similar for a divide.
Maths.h, should not change anything.
Functions are not actually included in the code, unless they are _used_. Maths.h, supplies functions for things like sin, cos, log etc.. Simple */+-, are part of the core compiler and do not want/use maths.h.
One trick I commonly use, is to remember that for an integer, division by 256, or 65536, can be done by just throwing away the bottom byte or bottom pair of bytes. So I've done (for example) fast PID maths by using an int24 (I coded a library for this), storing the PID factors as if they were integer 'two fifty sixths', instead of fractions, and then just taking the upper 16bits of the results. |
|
![](templates/subSilver/images/spacer.gif) |
uzicarl
Joined: 28 Mar 2017 Posts: 1 Location: Banned - spammer
|
|
Posted: Tue Mar 28, 2017 12:40 pm |
|
|
hi! I don't think you can avoid floats. I know that binary is fast but floats are necessary _________________ CA-banned-RL |
|
![](templates/subSilver/images/spacer.gif) |
newguy
Joined: 24 Jun 2004 Posts: 1903
|
|
Posted: Tue Mar 28, 2017 1:33 pm |
|
|
uzicarl wrote: | hi! I don't think you can avoid floats. I know that binary is fast but floats are necessary |
For angles, cos, sin, tan, etc.: maybe. For anything else, definitely not. You can accomplish the same thing using scaled integers much more quickly and with less memory usage.
Quite some time ago the dept's software engineer was responsible for taking GPS data and plotting locations on a map to determine the distance between locations. The data was made available on our internal CAN bus via a special module that interfaced to a GPS receiver. When he started calculating his distances, he was getting values on the order of a few hundred meters for points that were only 1 or 2 meters apart.
Turns out he was first casting the raw GPS data to a float, then using those floats in his calculations. Anyone who had (or still has) an HP calculator knows that, sometimes, when you enter a number the calculator will display that number with ".000000000001" added to it. When you're trying to pinpoint locations on the surface of the earth, that little bit of error really hurts you in the end. I told him to not cast the raw data to a float and try again. Turns out he then started getting numbers about what we were expecting. |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19358
|
|
Posted: Tue Mar 28, 2017 2:15 pm |
|
|
Sin's and cos can often avoid floats (binary scaled integers instead). The only thing I've has to use float for in about the last 10 years on a PIC, was when writing a logarithmic value.
It's worth understanding that people like NASA don't generally use floats. Nor do bankers. For both, the errors are too large, and they 'know' the maths ranges involved, so use fixed point arithmetic instead. |
|
![](templates/subSilver/images/spacer.gif) |
temtronic
Joined: 01 Jul 2010 Posts: 9168 Location: Greensville,Ontario
|
|
Posted: Tue Mar 28, 2017 3:20 pm |
|
|
Also... the CCS floating point isn't really good past what 4, 5 places? and of course the PIC wasn't designed for FP a zillion years ago.
Now if you added an external FPU...........
Jay |
|
![](templates/subSilver/images/spacer.gif) |
gjs_rsdi
Joined: 06 Feb 2006 Posts: 468 Location: Bali
|
|
Posted: Wed Mar 29, 2017 4:34 am |
|
|
Thank you all for the answers.
We had here in Bali the yearly "Silence day" were everything is silent, no traffic, no lights, I was out from Internet.
One of the functions is done already with simple maths, all divisions by 2, 4, 8 and multiplications by numbers from 1 to 15. Using also + & -.
Non passes 0.5 us
Remain three to complete, hope will succeed.
In another program for navigation I am using sin, cos, etc but the microcontroller doesn't have any time critical functions so floats are fine.
Again thank you for the help.
Joe |
|
![](templates/subSilver/images/spacer.gif) |
Marttyn
Joined: 06 Mar 2015 Posts: 28 Location: Spain
|
|
Posted: Wed Apr 15, 2020 3:24 pm |
|
|
Ttelmah wrote: | Immediately, unless for some reason interrupts are disabled.
This is where 'scaled integers' come in very useful.
Have a look in the manual 'common questions and answers'. 'How much time do maths operations take'.
For instance, suppose I want to divide by three.
Assume an int8 value, stored in an int16.
Now
value/3 will take about 20uSec.
(value*85), and then just read the high byte of the result, will take about 2uSec!...
Similarly if I want to print a number from an ADC reading as 0.00 to 5.00v. Assume reading is 10bit (0 to 1023).
Each step of the ADC is 0.0048828v.
Now three different solutions:
First just take the ADC reading, change to float, multiply by 5.0, divide by 1024.0, and print as a float. Slowest solution. The compiler will presolve partially but may well still divide by 204.8. This will take about 110uSec, then it'll have to do floating point divisions by 10.0 for each digit to output. So for three actual digits, perhaps 440uSec.
Second just use *0.0048828. Potentially a little faster. Multiplication is typically about 4* faster than division, but it still has to use /10.0 for each digit. So perhaps 350uSec.
Third use integers:
Take ADC_reading. Put into an int32. Multiply by 32000. Now just read the high 16bits of this (so effectively /65536, but involving no maths at all). Total time to do this, about 16uSec. Now print this 16bit value using %4.2LW. Uses 16bit integer divisions. About 76uSec total. |
Ttelmah, just amazing!
Can you share a little bit more of your knowledge? Specially how to "twist" the maths to make them more fast and efficient.
I, understand the first example, where you have:
result = value / 3
The same as:
result * 256 = value / 3 * 256
result * 256 = value * 85 <--- 256/3
result >> 8
well... more or less, but this is the core...
But don't understand the last example... why you multiply by 32000?
And can you show some more examples? or is there any good resource to find examples like this? Would like to learn to think "that way"
Thanks! |
|
![](templates/subSilver/images/spacer.gif) |
|
|
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
|