|
|
View previous topic :: View next topic |
Author |
Message |
Amolf
Joined: 09 Jun 2011 Posts: 3
|
signed shift right - compiler generates incorrect code! |
Posted: Wed Jun 22, 2011 3:36 am |
|
|
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
|
|
Posted: Wed Jun 22, 2011 5:48 am |
|
|
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
|
|
Posted: Wed Jun 22, 2011 6:54 am |
|
|
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
|
|
Posted: Wed Jun 22, 2011 9:49 am |
|
|
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
|
|
Posted: Thu Jun 23, 2011 5:48 pm |
|
|
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. |
|
|
|
|
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
|