Reputation: 1212
Some languages like Java, Verilog have both bitwise logical (<<, >>) and arithmetic shift (<<<, >>>) operators.
For unsigned values, logical and arithmetic shifts have identical operation. Say if 8'b11000101 is binary representation of 8-bit unsigned number 197, then
8'b11000101 >> 2 => 8'b00110001
8'b11000101 >>> 2 => 8'b00110001
8'b11000101 << 2 => 8'b00010100
8'b11000101 <<< 2 => 8'b00010100
For signed values, only the arithmetic and logical left shift operations are identical but arithmetic right shift leads to sign extension. Say if 8'b11000101 is binary representation of 8-bit signed number -59, then
8'b11000101 >> 2 => 8'b00110001
8'b11000101 >>> 2 => 8'b11110001
8'b11000101 << 2 => 8'b00010100
8'b11000101 <<< 2 => 8'b00010100
Python only has logical shift operators but no arithmetic shift operators. So how to achieve arithmetic right shift in python for signed and unsigned values ?
Upvotes: 10
Views: 14274
Reputation: 144540
Python only has logical shift operators but no arithmetic shift operators. So how to achieve arithmetic right shift in python for signed and unsigned values?
Python actually only has arithmetic shift operators:
n
is exactly the same as multiplying by 2 to the power n
for both positive an negative values.n
depends on whether the value being shifted is negative or not. Positive values are divided by 2 to the power of n
and rounded toward 0, whereas negative values behave as if there was an infinite string of 1
bits extending on the most significant bit side, a side effect of two's complement representation of negative numbers. This translates into a single mathematical equivalence: right shifting an integer by a positive integral quantity n
is evaluated as dividing it by 2 to the power of n
and rounding the result toward negative infinity, Math.floor(value / 2**n)
If you want to simulate unsigned right shift of negative values, as available in java and javascript, you must convert the negative value to a positive value with the fixed number of bits you are considering. Right shifting that will give the expected value:
x = -1
x32 = x & 0xffffffff # convert to 32-bit unsigned value
x >> 8 # produces -1
x32 >> 8 # produces 0x00ffffff
Upvotes: 12
Reputation: 53
I think the answer to your question depends on how you are storing your numbers. If you want 0b11000101
to represent -59
, then you need to specify the bit length somehow before performing the shift. Note that Python's binary representation of negatives is not in 2's complement format:
>>> bin(59)
'0b111011'
>>> bin(-59)
'-0b111011'
So, if -0b111011
works for your purposes, then you can perform the arithmetic right shift as specified in chqrlie's answer.
However, if you need your result to be in a 2's complement format, i.e. you need 0b11000101
arithmetic right-shifted by 2 to evaluate to literally 0b11110001
, then the following function should work for you:
# x is an n-bit number to be shifted m times
def sra(x,n,m):
if x & 2**(n-1) != 0: # MSB is 1, i.e. x is negative
filler = int('1'*m + '0'*(n-m),2)
x = (x >> m) | filler # fill in 0's with 1's
return x
else:
return x >> m
This basically performs the regular right shift, but fills in the leading zeroes with ones if the number is negative (in 2's complement format, with the specified bit length).
Examples:
sra(0b11000101, 8, 2) -> 0b11110001
sra(0x805a6cf3, 32, 0x10) -> 0xffff805a
sra(0x705a6cf3, 32, 0x10) -> 0x0000705a
Upvotes: 2
Reputation: 51835
not a python coder but I usually overcome this problem like this:
x = (x>>1)|(x&80); // 8bit
x = (x>>1)|(x&8000); // 16bit
x = (x>>1)|(x&80000000); // 32bit
so simply copy the MSb of original x
into space created by the bitshift. However this will work only for shifts by 1 bit. For more you need to do something like this:
if (x<0) x = (x>>2)|0xC0; else x = x>>2; // 8bit
if (x<0) x = (x>>2)|0xC000; else x = x>>2; // 16bit
if (x<0) x = (x>>2)|0xC0000000; else x = x>>2; // 32bit
if (x<0) x = (x>>3)|0xE0; else x = x>>3; // 8bit
if (x<0) x = (x>>3)|0xE000; else x = x>>3; // 16bit
if (x<0) x = (x>>3)|0xE0000000; else x = x>>3; // 32bit
if (x<0) x = (x>>4)|0xF0; else x = x>>4; // 8bit
if (x<0) x = (x>>4)|0xF000; else x = x>>4; // 16bit
if (x<0) x = (x>>4)|0xF0000000; else x = x>>4; // 32bit
if (x<0) x = (x>>5)|0xF8; else x = x>>5; // 8bit
if (x<0) x = (x>>5)|0xF800; else x = x>>5; // 16bit
if (x<0) x = (x>>5)|0xF8000000; else x = x>>5; // 32bit
...
You can create a LUT for the masks
LUT8[8]=
{
0x00,
0x80,
0xC0,
0xE0,
0xF0,
0xF8,
0xFC,
0xFE,
}
if (x<0) x = (x>>k)|LUT8[k&7]; else x = x>>k; // 8bit
The mask for bit-shift by k
bits is just number containing k
ones from MSb and then the rest are zeroes in binary.
There is also simpler solution (at cost of twice negating) like this:
if (x<0) x = -((-x)>>k); else x = x>>k;
Upvotes: -1