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

signed shift right - compiler generates incorrect code!

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



Joined: 09 Jun 2011
Posts: 3

View user's profile Send private message

signed shift right - compiler generates incorrect code!
PostPosted: Wed Jun 22, 2011 3:36 am     Reply with quote

I need to divide a number by 8, so I use the shift right command for speed, but the results are wrong for negative values !!!!

The compiler uses RRCF but fails to set the initial sign bit to the carry!
and it cloud have know because the type is signed int16!

Code:

   signed int16 value = -1600;
   signed int16  result; 
   result = value >> 3;       // divide by 8 should give -200
   printf("signed int16 shift right =  %ld\r\n", result);

Out: signed int16 shift right = 7992

I use PCW v4.099, for a PIC18F6527.
Is this a known fault? Anybody know a work around?

Best Regards,
Marco
Ttelmah



Joined: 11 Mar 2010
Posts: 19496

View user's profile Send private message

PostPosted: Wed Jun 22, 2011 5:48 am     Reply with quote

This is correct.....

You are wrong in your expectations.

If you read K&R (the original C book), you have:
"Right shifting an unsigned quantity, fills vacated bits with 0. Right shifting a signed quantity will fill with sign bits (arithmetic shift) on some machines such as the PDP-11, and with 0 bits (logical shift) on others."

The C standard, is to perform what the hardware supports.
The PIC performs a logical shift.

If you switch the compiler to ANSI mode, the extra code will be added to perform an arithmetic shift (this is the ANSI standard). Or you can simply do the same, by testing if the value is and masking the required bits yourself.

Best Wishes
Amolf



Joined: 09 Jun 2011
Posts: 3

View user's profile Send private message

PostPosted: Wed Jun 22, 2011 6:54 am     Reply with quote

Hi Ttelmah,

I did not know about the official/original C standard.
but I expected to keep the sign while shifting right..

After adding the line
#device ANSI
the result is still the same... positive!


Best Regards,
Marco
Ttelmah



Joined: 11 Mar 2010
Posts: 19496

View user's profile Send private message

PostPosted: Wed Jun 22, 2011 9:49 am     Reply with quote

Still right I'm afraid.
The compiler generates some extra code when ANSI is selected for a shift, so I thought it might be generating an arithmetic shift. However if you read the ANSI C manual, it says:

"If the right operand is negative, greater than, or equal to the length in bits of the promoted left operand, the result is undefined.".

So for ANSI C, the result of right shifting a negative value, is _undefined_.

You just have the wrong expectation about this. Basically in C, if you want to right shift a negative value, you have to do it yourself.
This is not a fault in the compiler, but you trying to do something that the language does not guarantee....

Worse in fact, the rotation wouldn't give the required division for a 'signed' number. Take for example -34. /8 = -4. The binary signed 16bit representations of these would be FFDE, and FFFC. While the 3 bit rotation of the former, even if you maintain the sign bit, would give FFFB. Not the value/8. Problem is that you are dealing with 2's complement notation. You don''t need to just maintain the sign bit, but convert the value to +ve, rotate this, and then convert it back.

Syntax:
Code:

signed int16 val;

    if (val>0)
       val>>=3;
    else
       if (val!=0) val=-((-val)>>3);


Alternatively:
Code:

int1 sign_flag;
signed int16 val;

    if (val<0) {
       sign_flag=true;
       val+=1;
    }
    else sign_flag=false;
    val>>=3;
    if (sign_flag) val|=0xE000;


The latter just provides the '1' offset needed by 2's complement notation, then sets the bits if the source is negative. More efficient in coding.

Just about every C reference book I know of, makes this fundamentally clear. Rotation can _only_ normally be used as an alternative to division for positive values.

Best Wishes
barryg



Joined: 04 Dec 2006
Posts: 41

View user's profile Send private message

PostPosted: Thu Jun 23, 2011 5:48 pm     Reply with quote

Compilers are pretty smart these days. You should test a few of these operations and look at the code that is generated. Try divides and multiplies, try right and left shifts.

What you will find is that the compiler will choose the most efficient method. If you divide an unsigned number by 2, it will use a shift instead. If you do the same thing with a signed number, it switches to division. I found this out after countless times trying to be smarter than the compiler.

It's called "premature optimization" and a web search on this topic will yield plenty of reading material. Smile
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