I'm trying to write a C function that will always find the arithmetic right shift of an unsigned integer argument, using bitwise operators. I managed to write a function that would always find the logical right shift of a signed integer using bitwise operators, but the same idea doesn't seem to be working for finding the arithmetic shift.
Here's my function right now:
unsigned arith(unsigned x, int k) {
unsigned x_arith = (int) x >> k;
x_arith = x_arith | ((-1<<(sizeof(x_arith)*8)-k)) & ~(-1<<(sizeof(x_arith)*8));
return x_arith;
}
Explanation:
(-1 << (sizeof(x_arith)*8-k))
Create an all-1's binary then shift those ones so that the first 1 aligns with the leftmost 1 in x_arth
.
& ~(-1 << (sizeof(x_arith)*8))
Create an all-1's binary and shift it so the first one aligns after the leftmost bit in x_arth
, then flip it, then keep only those bits in common with the binary above.
This whole process supposedly creates a mask that has 1's where x_arith
needs to have ones. Finally, I |
(or) the original x_arith
with this mask to get the 1's that I need to make the shift arithmetic and return the result.
However, the result is still a logical shift. I tried changing many things in the above code and got really frustrated with the weird numbers I got. What am I doing wrong?
UPDATE: The function posted above does actually find the arithmetic shift. The problem was that I was testing it with 4 and k=1, expecting the result to be 6, which turns out to be not how arithmetic shifting works. Here's a nicer-looking version of the function for future reference:
unsigned arith_shift(unsigned x, int k) {
unsigned x_arith = (int) x >> k;
int bits = sizeof(int) * 8;
unsigned mask = k? ((~0 << (bits-k)) & ~(~0 << bits)): 0;
return x_arith | mask;
}
Note that you can use both -1
and ~0
for an all-1's binary number. However, this function may behave differently on different machines since the behavior of left-shifting -1
is undefined (see e0k's answer).