|
|
View previous topic :: View next topic |
Author |
Message |
pmuldoon
Joined: 26 Sep 2003 Posts: 218 Location: Northern Indiana
|
integer math |
Posted: Tue Dec 14, 2010 9:30 am |
|
|
What would be the correct and safe way of doing the following:
I have an int16 and I want to get 75% of the value.
Code: | int16 NewVal,OldVal;
OldVal=1000;
NewVal= (OldVal/4)*3; |
Is it safe to assume the compiler will respect the parenthesis and never "pre-calculate" the constant math? |
|
|
newguy
Joined: 24 Jun 2004 Posts: 1909
|
|
Posted: Tue Dec 14, 2010 9:51 am |
|
|
Leave the division until the last step - less chance of losing precision that way. Other than that, what you have will do what you want. The only thing that I'd change would be to code the /4 as a shift by 2 (>> 2) which is much faster than a divide. That said, the compiler may be smart enough to do the divide in this manner... |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19552
|
|
Posted: Tue Dec 14, 2010 11:08 am |
|
|
The compiler does use >> for the binary divisions and multiplications.
It might be 'worth' seeing just how good the optimisation is, and comparing using MPLAB, the code produced for:
Code: |
int16 NewVal,OldVal;
OldVal=1000;
NewVal= (OldVal + (OldVal*2))/4;
|
With using the *3. I think you may well find this is faster.
Best Wishes |
|
|
pmuldoon
Joined: 26 Sep 2003 Posts: 218 Location: Northern Indiana
|
|
Posted: Tue Dec 14, 2010 11:15 am |
|
|
Since the division is a power of 2, i figured there'd be no loss of precision doing it first. Also figured i wouldn't have to worry about overflow. Although, since i'm using it on an AD reading (10-bit) guess that's not possible.
I was just wondering if the compiler might be smart enough to just create a constant 0.75 (float), or worse 0 (integer) and eliminate the individual steps. As it may in an expression that might be written for clarity, like:
Minutes = Days * 24 * 60;
Where the compiler (I hope) would convert 24*60 into a single constant of 1440 and just use that instead. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Dec 14, 2010 2:40 pm |
|
|
Compile the program and look at the .LST file. It clearly shows the
right-shift is done before the multiply.
Code: |
.................... NewVal= (OldVal/4)*3;
// Rotate OldVal to the right by 2 bits.
003C: RRCF OldVal+1,W
003E: MOVWF 0A
0040: RRCF OldVal,W
0042: MOVWF 09
0044: RRCF 0A,F
0046: RRCF 09,F
// Then clear the top two bits in the MSB.
0048: MOVLW 3F
004A: ANDWF 0A,F
004C: MOVFF 0A,0C
0050: MOVFF 09,0B
0054: CLRF 0E
0056: MOVLW 03 // Setup to multiply by 3
0058: MOVWF 0D
005A: BRA 0004 // Jump to a Multiplication routine at 0004
005C: MOVFF 02,NewVal+1
0060: MOVFF 01,NewVal
------------
// Multiplication routine at 0004:
0004: MOVF 0B,W
0006: MULWF 0D
0008: MOVFF FF3,01
000C: MOVFF FF4,00
0010: MULWF 0E
0012: MOVF FF3,W
0014: ADDWF 00,F
0016: MOVF 0C,W
0018: MULWF 0D
001A: MOVF FF3,W
001C: ADDWFC 00,W
001E: MOVWF 02
0020: GOTO 005C (RETURN)
|
Test program:
Code: |
#include <18F452.h>
#fuses XT,NOWDT,PUT,BROWNOUT,NOLVP
#use delay(clock=4000000)
//=====================================
void main()
{
int16 NewVal,OldVal;
OldVal=1000;
NewVal= (OldVal/4)*3;
while(1);
} |
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19552
|
|
Posted: Tue Dec 14, 2010 3:36 pm |
|
|
pmuldoon wrote: | Since the division is a power of 2, i figured there'd be no loss of precision doing it first. Also figured i wouldn't have to worry about overflow. Although, since i'm using it on an AD reading (10-bit) guess that's not possible.
I was just wondering if the compiler might be smart enough to just create a constant 0.75 (float), or worse 0 (integer) and eliminate the individual steps. As it may in an expression that might be written for clarity, like:
Minutes = Days * 24 * 60;
Where the compiler (I hope) would convert 24*60 into a single constant of 1440 and just use that instead. |
Think about it.
You have '3'.
Divide it by 4 (integer maths), and you have 0.
Then multiply by 3, and you have 0.
However multiply first, and you have 9. Then divide by four, and you get 2.
It is _vital_ with integer maths to always perform multiplication stages first. Otherwise any decimal parts are lost.
Yes, the compiler would convert a pair of multiplied constants, into a single constant.
However pregenerate a float constant, for a sum involving only integers, no. This would be awful behaviour (look at how slow and bulky float arithmetic is), and would change the rules regarding arithmetic types. The _highest_ type involved in each side of a sum, is the type used for the arithmetic in C. This applies for the 'pre-solved' sections as well.
Best Wishes |
|
|
FvM
Joined: 27 Aug 2008 Posts: 2337 Location: Germany
|
|
Posted: Wed Dec 15, 2010 12:45 am |
|
|
Referring to the original question, you can be sure, that the compiler actually is respecting the order of execution dictated by the parenthesis. Ignoring it would be a serious violation of essential C rules and make a compiler effectively useless.
The precision requirement however demands to multiply first
Code: |
result = (adc_val*3)/4
|
as Ttelmah explained. As you said, overflow must not be feared for a 10-Bit ADC value. If the input value covers the full 16 Bit range, the conversion must use an int32 intermediate result. |
|
|
pmuldoon
Joined: 26 Sep 2003 Posts: 218 Location: Northern Indiana
|
|
Posted: Wed Dec 15, 2010 7:53 am |
|
|
Okay, that was embarassing, I'm an idiot!
I don't know where my head was with the math.
Just didn't "Think about it" as Ttelmah pointed out. Thanks, Ttelmah.
Can I just blame it on an imbalance of caffeine & sugar?
Maybe too much eggnog, if I knew what THAT was.
And thank you FvM for answering my original question (which I didn't state very clearly). I was just wanting to be SURE since I really haven't ever used multiple constants in an expression that COULDN'T be consolidated by the compiler. And we all have seen compilers get confused from time to time. I could have read the lst and see that it worked ONE time correctly. but I thought it would just be an "off the top of your head" kind of thing for the forum.
And PCM - I would like to see you in action some time because I think you can turn a question into a program, list it, and paste it into a concise reply faster than anyone I know. Thank you for the example.
And I like the example from Ttelmah. I've seen it before, probably in another of your replies to somebody's question. But I like it cuz I always have to look at it twice to get why it's faster. Then I get it.
And Newguy. The fastest reply-er and first to comment on my dumb math mistake.
believe it or not, that bad math example was right out of a program I am writing.
Believe it or not, with the range of values and input signal it really didn't hurt anything being off up to a few counts.
Believe it or not, I fixed it cuz it would have bothered me knowing it was in there.
Believe it or not, that is the LAST dumb mistake I plan to make this year. (keyword: plan).
Thanks, guys.
Happy holidays to all! |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19552
|
|
Posted: Wed Dec 15, 2010 10:14 am |
|
|
I have used variants of this many times. More recently, CCS have started using it. In one of their include files, there is a fast *10, using int32 maths, that uses the same idea. It is an area, where if their optimiser was _really_ smart, it could look at any constant integer multiplication, using small values, and see if a sum of binary multiples could be substituted. However lacking this, we can do it ourselves!....
Best Wishes |
|
|
|
|
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
|