I ran into a strange problem. To demonstrate, let's take the largest unsigned number on my machine (printf "%X \n" -1
gives me FFFFFFFFFFFFFFFF
), and try to shift some bits. First, shift to the left:
printf "%X \n" $(( 0xFFFFFFFFFFFFFFFF<<4 ))
FFFFFFFFFFFFFFF0
printf "%X \n" $(( 0xFFFFFFFFFFFFFFFF<<8 ))
FFFFFFFFFFFFFF00
printf "%X \n" $(( 0xFFFFFFFFFFFFFFFF<<16 ))
FFFFFFFFFFFF0000
So far so good. As expected. Now let's try the right shift:
printf "%X \n" $(( 0xFFFFFFFFFFFFFFFF>>4 ))
FFFFFFFFFFFFFFFF
printf "%X \n" $(( 0xFFFFFFFFFFFFFFFF>>8 ))
FFFFFFFFFFFFFFFF
printf "%X \n" $(( 0xFFFFFFFFFFFFFFFF>>16 ))
FFFFFFFFFFFFFFFF
Wait, what?? Why is this not working? Is that a bug?
Edit:
I am dreading that someone will suggest some connection with the sign bit being raised. But we are not talking about arithmetic, so the concept of sign has no place here. Other tools like *
and /
are for arithmetic. The whole point of having a tool that can manipulate bits is to be able to manipulate bits -- no matter how I'll chose to display those bits later, as signed or as unsigned. Right? Like:
printf "%u \n" -1
18446744073709551615
Any ideas anybody?
EDIT:
Since the answers here went straight to talking about multiplication or division, let me try to explain my concern more clearly. Multiplication/division and bit-shifting are two different things, although I can see the connection between them in the minds of long-time programmers. When doing arithmetic, you have to have the concept of sign; for bit-shifting you don't. Bash has given us two distinctly different sets of tools for these two different things. When I want to multiply a number by 2, I reach for the *
tool. The fact that under the hood Bash can use bit-shifts for arithmetic is beyond the point.
To quote one of the answers...
If the sign bit wasn't copied, the result would turn into an unsigned number. E.g. shifting the 8-bit value
1111 0000
once to the right would give0111 1000
But turning 1111 0000
into 0111 1000
is exactly what I want. If I wanted to do a division, then I would use arithmetic operstor instead.
Anyway, is there at least some way of explicitly specifying with what kind of bits it should fill when shifting?
printf "%X \n" $(( 0x7FFFFFFFFFFFFFFF>>16 ))