[Tutor] << operator ? [left bitwise shifting]
Paul Sidorsky
paulsid@shaw.ca
Wed, 10 Apr 2002 23:42:32 -0600
Erik Price wrote:
> But now I'm just curious what it's for -- why you would use it? Just to
> double numbers? And if that's the case, then what's the right bitwise
> shift operator for?
Both are used extensively for working with binary protocols, hardware,
compact binary file formats, etc. These don't show up very often in
Python, which is why many people may never see it in use. Nevertheless
here's an example, with roots in the real world:
A common thing in hardware is to query a device (e.g. the video card)
and get back a byte, word, or dword with a bunch of status information.
The information is given in this compact form because hardware access is
relatively expensive and space is usually at a premium.
Say we know that when we request a status from the video card we get
back a 16-bit word with unused bits 14-15 (the leftmost) the current
video mode is in bits 10-13, the maximum possible number of buffers for
the current mode is in bits 8-9, and the current vertical refresh rate
in Hz is in bits 0-7 (the rightmost). (Don't worry if you don't know
what any of this means, but do note that bits are numbered from 0 to 15
going from right to left if you didn't know that.) We want to store
these numbers in Python variables. Say also that we've stored this
status word in a variable called 'status', but for annotation purposes
I'll just assign status a value so we can watch how it gets used.
Here's how this would be done:
status = 0x2746 # In binary, status = 0010 0110 0100 0110
# Isolate bits 0-7.
t = status & 0x00FF # t = binary 0000 0000 0100 0100 = 70
# Refresh rate is in lowest bits, so no shifting needed.
refreshrate = t # 70 Hz vertical refresh.
# Now isolate bits 8-9.
t = status & 0x0300 # t = binary 0000 0010 0000 0000 = 0x0200 = 512
# Shift the number of buffers so it makes sense.
t = t >> 8 # t = binary 0000 0000 0000 0010 = 2
buffers = t # 2 video buffers, we can use double-buffering!
# Lastly, isolate bits 10-14.
t = status & 0x3C00 # t = binary 0010 0100 0000 0000 = 0x2400 = big!
# Shift the video moder number into place.
t = t >> 10 # t = binary 0000 0000 0000 1001 = 9
videomode = t # Video mode #9, whatever that means.
Of course we could have done all of this by division, but this is a bit
clearer. Since we know the number of buffers starts in bit 8 and the
video mode starts in bit 10, once we isolate those bits we can just
shift the result by 8 or 10 bits, respectively, to get the actual value.
If we were later going to set the status and had to pass a status word
with of the same format, we can use this code to build the new status
word:
status = (videomode << 10) | (buffers << 8) | refreshrate
Again, we could do this by multiplication but using the shift operator
is somewhat more straightforward.
The clarity of videomode << 10 over videomode * 2**10 is debatable, but
what isn't debatable is the speed gain. Most processors support
shifting as built in instructions, and these instructions are almost
always a LOT faster than general multiplication, because it's easy to
build very fast circuitry that can shift bits around.
Thus this kind of thing shows up a lot in C and assembler code. In
fact, for very performance-intensive code programmers will often use
shift operations to do their multiplication and division! x>>1 is an
extemely common and extremely fast integer divide-by-2, and x<<2 makes
for a quick multiply-by-4. (Notice I didn't put spaces around the
operators. It's a a curious phenomenon that programmers writing some
form of optimized code tend to optimize on whitespace as well; I've
almost never seen the properly-spaced version in optimized code.)
Searching the entire standard library for << produced only about 30
hits. So don't worry about it too much. Odds are you probably won't
need to shift bits, at least when working with Python.
--
======================================================================
Paul Sidorsky Calgary, Canada
paulsid@shaw.ca http://members.shaw.ca/paulsid/