CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

integer math

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
pmuldoon



Joined: 26 Sep 2003
Posts: 218
Location: Northern Indiana

View user's profile Send private message

integer math
PostPosted: Tue Dec 14, 2010 9:30 am     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Dec 14, 2010 9:51 am     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Dec 14, 2010 11:08 am     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Dec 14, 2010 11:15 am     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Dec 14, 2010 2:40 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Dec 14, 2010 3:36 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Wed Dec 15, 2010 12:45 am     Reply with quote

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

View user's profile Send private message

PostPosted: Wed Dec 15, 2010 7:53 am     Reply with quote

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

View user's profile Send private message

PostPosted: Wed Dec 15, 2010 10:14 am     Reply with quote

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
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
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