|
Working with Bits
Here is a listing of all of the bitwise operators (bitops) that VC supports, followed by a description and an example: *Note: the words in CAPITAL letters and the symbol which follows it in parentheses can be used interchangably*
AND (&) - This is the bitwise AND (not to be confused with && which represents the logical (or boolean) AND) You would use this to check a number to see if a particular bit is set.
If, for example, a bit in number1 and a bit in number2 are both set, then that bit in the resulting number would be set to 1. If the bit in either or both numbers is 0, then the resulting bit would be 0 as well. This bitop, and indeed most all of them, is best taught by example, so study the following and then re-read the description to best understand this operation:
If
x = 1010b
y = 0110b
and
z = x & y
then z is equal to
1010
0110
----
0010
OR ( | ) - This is the bitwise OR (not to be confused with the boolean or logical OR which is denoted by two pipes - ||). When this is used, if the bit your checking in either number is set (or both are set) that bit will be set in the resulting number. Again this one is best taught by example:
(1010 | 0110) = 1110b
Or, in decimal numbers:
(10 | 6) = 14
XOR ( ^ ) - This is the Exclusive OR. This operates the exact same as the bitwise OR except that it will only set a bit if they're different, as shown in the following example:
(1010 ^ 0110) = 1100b
or, in decimal:
(10 ^ 6) = 12
Now, while VC does NOT support the Complement Operator (which sets every bit that's a 0 to a 1 and vice versa) that can easily be achieved by XORing the number you would want to complement with a bitmask (or basically a number that would set all of the bits to 1. i.e. 15 for 4 bits, 255 for 8 bits, 65535 for 16 bits, etc...). You can see how it would work in the following example:
1010 ^ 1111 = 0101b
or, again, in decimal:
10 ^ 15 = 5
Next, there are two operators (<< and >>) that are used to "shift" bits to the left or right. Shifting the bits in a number to the left has the effect of multiplying the number by 2n where n is the number of bits shifted. Shifting them to the right does the same thing except it divides rather than multiplies. Here's a quick example of their effect (*Note: 8 bits are used in the following example*):
(0000 0101 << 2) = 0001 0100b
Or, as they may be shown in decimal:
(10 << 2) = 20 (10 * 21 = 10 * 2 = 20)
One thing to not, however, is that bits at then beginning or end of a number will actually "fall-off" of the number when they're shifted. So, for example, (1010 1010 > 1) becomes 0001 1011.
 So, Why Would I Want To Use Them?
Okay, so now that you know what all of the various bitops are, let's go more in-depth on what you would hope to achieve by using them. Well, to begin with, (x
 Bitflags and how to use them
Let's take a look at how you would go about setting or clearing a particular "flag" (for sanity's sake, we'll only work with the first 8 bits of the integers).
The first thing that you could do to make your life easier (and your code more readable) is to use #defines to assign the individual bits names. This is how you could go about doing that:
#define GotGoldKey (1 << 0)
#define GotRedKey (1 << 1)
#define GotWhiteKey (1 << 2)
Another way to do it would be to use the actual decimal equivalents:
#define GotGoldKey 1
#define GotRedKey 2
#define GotWhiteKey 4
Whichever way you prefer works just fine. Just as long as you use the number for the "place" of the bit you want to change or check. Now, let's say the player got the RED key. We can set that bit by using the following function: (assumes a global integer variable called gFlags)
void ToggleFlag(int flag)
{ int f;
f = flag ^ $FFFFFFFF;
gFlags = gFlags ^ f;
}
Which would be used like this:
ToggleFlag(GotRedKey);
Now, in the above function, you may have noticed that I declared a local variable, f, and then set it equal to flag XOR'd with $FFFFFFFF. What that did was made f equal to the complement of flag ($FFFFFFFF is the hexidecimal value of a 32-bit integer in which all the bits are set to 1. I just used $FFFFFFFF because it's a lot prettier than its decimal equiv which is 4294967295) *Note: You may want to set up a #define which contains that number so you don't have to re-type it in everytime you want to make a complement*
Why would I do that, you might be asking. Well, the answer is simple. In order to better understand it, let's take a look at the last 4 bits of the numbers we just played with:
flag = 0010b
f = 0010 ^ 1111 = 1101b
gFlags = 0000 ^ 1101 = 0010b
Did you see how that worked? It's the same principle behind a photo negative. You have the original image (in this case 0010) and, in order to make copies of it, you need to take a negative of it (which is the complete inverse or 1101). Then when you shine a light through the negative onto responsive paper (gFlags) the result is a positive (0010). In order
I showed you that method simple to demonstrate the usefulness of making a complement. However, there exists a far easier method. That same effect can be achieved by adding the #define'd bit value to gFlags, as demonstrated in the following example:
0010
+0000
-----
0010
Okay, so your asking "Then why bother with the first method at all?" There is one tremendous advantage that the first method has over this method. It can be used not only to set the bit, but to clear it as well. Look at the following example which uses the complement of the flag 0010 (1101) and the contents of gFlag (0010):
1101 ^ 0010 = 0000
So, by using complements you save yourself the trouble of not only writing two functions (one to add and one to subtract the flags) but you also save yourself the trouble of determining which function needs to be used when. With the above function, you can flip any bit in the integer by simply calling the function and passing to it the bit you want to flip.
 Okay, so now that I've set bits, how do I check to see which ones are set?
Okay, now that we know how to toggle flags, let's look at how to check them. Remember how the bitwise & will only set the bit to 1 if that bit in both the numbers is 1, otherwise it sets it to 0. You can use that fact to your advantage by looking at it this way: If we & a number we want to check with a number whose only set bit is the one we want to check, then the resulting number will only be non-zero if that bit is set to 1 in BOTH the number we want to check and the number we're using to check. I can only imagine how confusing that phrase just was, so here's a sample function to check a bit:
int ReadFlag(int flag)
{ return(flag & gFlags);
}
And you could use it like this:
if(ReadFlag(GotGoldKey))
// Do stuff if it's set
So, what does that accomplish? Well, let's take a look at the bits by assuming that gFlags is already set to 0101 and we want to see if the player has the Gold Key (from previous section):
flag = 0001 (gold key)
gFlags = 0101
0001 & 0101 = 0001b
Which means that the function would return a value. Often times you can just check to see if the return value is 0. If it is, then that bit is NOT set. If it returns a number, though, then that bit is set, and the number returned is the same number as the flag passed to it. Let's see how the function might return a 0 value by checking to see if the player has the RED key:
flag = 0010
gFlags = 0101
0010 & 0101 = 0000b
Well, that's all that I can think of to write at the moment, but if I think of anything else, I'll update this article, so check back now and then to see if anything new was added.
I sincerely hope that this article helped you to better understand how to, and why to, use bitwise operators in VC, and I wish you the best of luck on your endevours with VergeC.
|