News:

Let's find out together what makes a PIC Tick!

Main Menu

Right shift on signed number

Started by broderic, Jun 16, 2021, 07:08 AM

Previous topic - Next topic

broderic

Hello.
I have doubt on this expression, used in compiler code:  a >> 2.
If a is defined as sbyte, when a = 4 I have the proper result (1), but when a = -4, I would expect -1 as a result.
I don't understand why I don't have that, and generally how the right shift of negative number is treated.

Thank you

Regards



towlerg

Bit manipulation of signed numbers will have (apparently) unpredictable result. For instance changing the MSB will change the sign.

Yasin

so as not to lose the mark. It be safe to store the sign bit before the shift and then restore it.

Stephen Moss

Quote from: broderic on Jun 16, 2021, 07:08 AMI don't understand why I don't have that, and generally how the right shift of negative number is treated.
If you read the Bitwise Shift Right section of the manual you will see that When you shift bits to the right the new bits shifted in to the left are 0.
Consequently, if the sign bit for a positive number is a 0 you will get the correct result with a positive number, i.e.
00000100 (4) becomes 00000001 (1)
That would mean the sign bit of a negative number is a 1, shifting right both changes the sign bit as 0 are shifted in & the sign bit then become part of the number value and so...
10000100 (-4) becomes 00100001 (33)

I don't know if this would work but you could try a divide by 2, 4, 8, 16 an so on instead to shift the bits 1, 2, 3, 4 places and see if that retains the sign bit but whether or not that may be a suitable solution depends on what you are shifting any why.

trastikata

QuoteNote.
Bitwise operations are not permissible with floating point constants or variables. All bit shifts are
unsigned, regardless of the variable type used.

Shifts may have the meaning of division or multiplication by 2^n on unsigned variables. However this is not the intent of the command itself, and there is no way the compiler can know if you want to make a division/multiplication or just a shift. For example many sensor registers share space for different commands and you need to shift the bits to get/set the command.

Thus, if you need it for division/multiplication by 2^n of signed variables, you will need to keep track of the sign bit.

tumbleweed

Quote10000100 (-4) becomes 00100001 (33)

Just to be clear, negative numbers are represented using 2's compliment representation,
so while the msb lets you know if the value is +/-, there's more to it than that.

Take -4 = 0xFC = 1111 1100 in 2's compliment

Doing an unsigned '>> 2' shift on that gets you 0011 1111 = 0x3F = 63
If right-shift supported signed values that would have been 1111 1111 = 0xFF = -1

top204

#6
From page 86 of the Positron8 compiler's manual: "Bitwise Shift Right '>>'"

"Note.
Bitwise operations are not permissible with floating point constants or variables. All bit shifts are
unsigned, regardless of the variable type used."

Does anyone actually read what I take time to write in the manuals? :-)

Signed right shifts are supported with Positron16 because of the excellent architecture and mnemonics of the dsPIC and PIC24 devices, but I am still working on signed shift right for the Positron8 compiler. Because of its 8-bit RISC architecture, keeping the 2s complement of a right shift is rather code consuming and not as easy to write the code for all the variable types that must be operands and assignments and shift values. Actually a hundred or so different functions within the compiler's source for each iteration of a signed shift right operator, and the different mnemonics required for the different 8-bit PIC device families!

When I first started writing the code for signed right shifts in Proton24, many years ago, I was amazed at how many other PIC C compilers also do not keep the sign from a right shift, and still don't.

trastikata

Quote from: top204 on Jun 16, 2021, 11:22 AMbut I am still working on signed shift right for the Positron8 compiler.

Hello Les, are you going to implement a different command?

As I mentioned in my earlier post if the shift is not intended as a mathematical manipulation but simple shift, this could cause a problem. In many sensors and ICs half the register is used for one command, the other half for another, getting the half that you need by shifting bits left and right is very handy.

top204

#8
No extra command is required.

A signed shift right has exactly the same syntax as an unsigned shift right, except it must first examine the MSB (Most Significant Bit) of a signed variable type and if it is set, it is holding a negative value, so the shifting must set the Most Significant Bits that are shifted right as well, so the shifted result value is also negative. i.e. Sign extending.

Sounds easy, and it is with Byte variables that are shifted by a Constant only, but with all the other types of variables and constants at all the operator's locations, it can get quite complex and system variables are sometimes required to store variable contents, otherwise, they get over-written with the shifts, and the MSB is lost.

Signed shifting will only happen with signed variables, and if an unsigned variable is used, the compiler will shift it unsigned, just the same as it does with expression operators. That is why mixed signed and unsigned expressions are a "no-no" in all compilers because the compiler will default to a signed expression if it sees a signed variable within an expression, but other compilers, sometimes, default to unsigned expressions if a signed variable is used within an expression with unsigned variables, and some vice-versa.

To get the quivalent of shifting a signed variable, simply use the divide operator. For example:

SignedVar = SignedVar >> 1, is the same as: SignedVar = SignedVar / 2
SignedVar = SignedVar >> 2, is the same as: SignedVar = SignedVar / 4
SignedVar = SignedVar >> 3, is the same as: SignedVar = SignedVar / 8
SignedVar = SignedVar >> 4, is the same as: SignedVar = SignedVar / 16
SignedVar = SignedVar >> 5, is the same as: SignedVar = SignedVar / 32
SignedVar = SignedVar >> 6, is the same as: SignedVar = SignedVar / 64
etc...

Left Shifts are always unsigned, regardless if the variable is signed or unsigned because they immitate a multiplication, and a left shift will always remove the MSB of a variable and add a new LSB.

broderic

Thanks a lot, Les, for your kind explanation (next time I will read the manual more accurately:-)).

Thank you towlerg, Yasin, Stephen, trastikata, tumbleweed for your kind attention and suggestions.

Regards.