Shifting Bits
Occasionally, you need to shift bits. This bit operation doesn’t come up that often in Basic, but when you need to get a certain bit into a certain position, nothing else will do.
Shifting left 1 bit means multiplying by 2, shifting left 2 bits means multiplying by 4, and so on. Similarly, shifting right 1 bit means dividing by 2, and so on. In Basic terms, the operation starts out looking simple:
Function LShiftWord1(w As Integer, c As Integer) As Integer
LShiftWord1 = w * (2 ^ c)
End Function
Function RShiftWord1(w As Integer, c As Integer) As Integer
RShiftWord1 = w \ (2 ^ c)
End Function
These functions work as long as you feed them positive integers and the results come out as positive integers. Otherwise, things go wrong. Before we get to the real version, let’s take a look at a C++ version:
// Shift bits of DWord right
DWORD DLLAPI RShiftDWord(DWORD dw, unsigned c)
{
return dw >> c;
}
That’s it. Even if you’re not a C++ programmer, you can probably guess the code for LShiftDWord, RShiftWord, and LShiftWord. There’s still a function call for shifting. What Visual Basic really needs are Shl and Shr operators as in most other languages. Then the work would be done automatically inline. Unfortunately, you have to do the messy job yourself if you’re not willing to add a C++ DLL.
Function LShiftWord(ByVal w As Integer, ByVal c As Integer) As Integer
BugAssert c >= 0 And c <= 15
Dim dw As Long
dw = w * Power2(c)
If dw And &H8000& Then
LShiftWord = CInt(dw And &H7FFF&) Or &H8000
Else
LShiftWord = dw And &HFFFF&
End If
End Function
Notice that this function uses the Power2 property to look up powers of 2 rather than calculating them. See “Module initialization versus component initialization” earlier in this chapter.
Since shifting is faster than the equivalent multiplication and
division operations in assembly language, hardcore programmers in other languages often shift bits to optimize multiplication and division by multiples of 2. But you can see why the functions I just mentioned are useless for optimization: it doesn’t do any good to fake division by shifting if your shift function fakes shifting by dividing.
The function works by doing the arithmetic on a temporary Long variable and then converting the result back to an Integer. But you can’t do that for the LShiftDWord and RShiftDWord functions because there isn’t a larger integer size to cast down from. The functions have to use error trapping to identify overflow, and they will fail on some input. It’s the best I could do, and rarely will you ever hit the limits.
The Win32 GetLogicalDrives function returns a Long in which the corresponding bit is set for each existing drive. The easiest way to check those drives is to loop through all 26 bits, shifting them one at a time into the rightmost position and then testing them. Here’s a function that translates bits into a more Basic-like string of pluses and minuses:
Function VBGetLogicalDrives() As String
Dim f32 As Long, i As Integer, s As String
f32 = GetLogicalDrives()
For i = 0 To 25
s = s & IIf(f32 And 1, “+”, “-”)
f32 = RShiftDWord(f32, 1)
Next
VBGetLogicalDrives = s
End Function
The CDrives class described in Chapter 4 has a more realistic example of RShiftDWord and GetLogicalDrives.