Bitwise Operators in C and C++
Bitwise Operators in C and C++
Tutorial
Generally, as a programmer you don't need to concern yourself about operations at the bit level.
You're free to think in bytes, or ints and doubles, or even higher level data types composed of a
combination of these. But there are times when you'd like to be able to go to the level of an individual
bit. Exclusive-or encryption is one example when you need bitwise operations.
Another example comes up when dealing with data compression: what if you wanted to compress a
file? In principle, this means taking one representation and turning it into a representation that takes
less space. One way of doing this is to use an encoding that takes less than 8 bits to store a byte. (For
instance, if you knew that you would only be using the 26 letters of the Roman alphabet and didn't
care about capitalization, you'd only need 5 bits to do it.) In order to encode and decode files
compressed in this manner, you need to actually extract data at the bit level.
Finally, you can use bit operations to speed up your program or perform neat tricks. (This isn't always
the best thing to do.)
Bitwise AND
The bitwise AND operator is a single ampersand: &. A handy mnemonic is that the small version of the
boolean AND, &&, works on smaller pieces (bits instead of bytes, chars, integers, etc). In essence, a
binary AND simply takes the logical AND of the bits in each position of a number in binary form.
For instance, working with a byte (the char type):
01001000 &
10111000 =
-------00001000
The most significant bit of the first number is 0, so we know the most significant bit of the result must
be 0; in the second most significant bit, the bit of second number is zero, so we have the same result.
The only time where both bits are 1, which is the only time the result will be 1, is the fifth bit from the
left. Consequently,
72 & 184 = 8
Bitwise OR
Bitwise OR works almost exactly the same way as bitwise AND. The only difference is that only one of
the two bits needs to be a 1 for that position's bit in the result to be 1. (If both bits are a 1, the result
will also have a 1 in that position.) The symbol is a pipe: |. Again, this is similar to boolean logical
operator, which is ||.
01001000 |
10111000 =
-------11111000
and consequently
72 | 184 = 248
Let's take a look at an example of when you could use just these four operators to do something
potentially useful. Let's say that you wanted to keep track of certain boolean attributes about
something -- for instance, you might have eight cars (!) and want to keep track of which are in use.
Let's assign each of the cars a number from 0 to 7.
Since we have eight items, all we really need is a single byte, and we'll use each of its eight bits to
indicate whether or not a car is in use. To do this, we'll declare a char called in_use, and set it to zero.
(We'll assume that none of the cars are initially "in use".)
char in_use = 0;
Now, how can we check to make sure that a particular car is free before we try to use it? Well, we
need to isolate the one bit that corresponds to that car. The strategy is simple: use bitwise operators
to ensure every bit of the result is zero except, possibly, for the bit we want to extract.
Consider trying to extract the fifth bit from the right of a number: XX?XXXXX We want to know what
the question mark is, and we aren't concerned about the Xs. We'd like to be sure that the X bits don't
interfere with our result, so we probably need to use a bitwise AND of some kind to make sure they
are all zeros. What about the question mark? If it's a 1, and we take the bitwise AND of XX?XXXXX
and 00100000, then the result will be 00100000:
XX1XXXXX &
00100000 =
-------00100000
Whereas, if it's a zero, then the result will be 00000000:
XX0XXXXX &
00100000 =
-------00000000
So we get a non-zero number if, and only if, the bit we're interested in is a 1.
This procedure works for finding the bit in the nth position. The only thing left to do is to create a
number with only the one bit in the correct position turned on. These are just powers of two, so one
approach might be to do something like:
int is_in_use(int car_num)
{
// pow returns an int, but in_use will also be promoted to an int
// so it doesn't have any effect; we can think of this as an operation
// between chars
return in_use & pow(2, car_num);
}
While this function works, it can be confusing. It obscures the fact that what we want to do is shift a
bit over a certain number of places, so that we have a number like 00100000 -- a couple of zeros, a
one, and some more zeros. (The one could also be first or last -- 10000000 or 00000001.)
We can use a bitwise leftshift to accomplish this, and it'll be much faster to boot. If we start with the
number 1, we are guaranteed to have only a single bit, and we know it's to the far-right. We'll keep in
mind that car 0 will have its data stored in the rightmost bit, and car 7 will be the leftmost.
int is_in_use(int car_num)
{
return in_use & 1<<car_num;
}
Note that shifting by zero places is a legal operation -- we'll just get back the same number we started
with.
All we can do right now is check whether a car is in use; we can't actually set the in-use bit for it.
There are two cases to consider: indicating a car is in use, and removing a car from use. In one case,
we need to turn a bit on, and in the other, turn a bit off.
Let's tackle the problem of turning the bit on. What does this suggest we should do? If we have a bit
set to zero, the only way we know right now to set it to 1 is to do a bitwise OR. Conveniently, if we
perform a bitwise OR with only a single bit set to 1 (the rest are 0), then we won't affect the rest of
the number because anything ORed with zero remains the same (1 OR 0 is 1, and 0 OR 0 is 0).
Again we need to move a single bit into the correct position: void set_in_use(int car_num) { in_use =
in_use | 1<<car_num; }
What does this do? Take the case of setting the rightmost bit to 1: we have some number 0XXXXXXX |
10000000; the result, 1XXXXXXX. The shift is the same as before; the only difference is the operator
and that we store the result.
Setting a car to be no longer in use is a bit more complicated. For that, we'll need another operator.
Summary
You should now be familiar with six bitwise operators:
Works on bits for left argument, takes an integer as a second argument
bit_arg<<shift_arg
Shifts bits to of bit_arg shift_arg places to the left -- equivalent to multiplication by 2^shift_arg
bit_arg>>shift_arg
Shifts bits to of bit_arg shift_arg places to the right -- equivalent to integer division by 2^shift_arg
Works on the bits of both arguments
left_arg & right_arg
Takes the bitwise AND of left_arg and right_arg
left_arg ^ right_arg
Takes the bitwise XOR of left_arg and right_arg
left_arg | right_arg
Works on the bits of only argument
~arg
Reverses the bits of arg
Skills and knowledge You also know a couple of neat tricks that you can use when performance is
critical, or space is slow, or you just need to isolate and manipulate individual bits of a number.
And you now should have a better sense of what goes on at the lowest levels of your computer.
A Parting Puzzle
One final neat trick of bitwise operators is that you can use them, in conjunction with a bit of math, to
find out whether an integer is a power of two. Take some time to think about it, then check out the
solution.