Hi all. I wrote this to expand on what I did with the even/odd problem and hopefully to spread the word about bits, bitwise operators and binary arithmetic. I'll come clean, I'm not a n00b, I've been programming for about 26 years (since I was a little kid with my ZX Spectrum) and for the last 10 of those years I've been a professional game developer. I did come here, however, to try and help out any newbies specifically with parts of programming that I think are becoming lost arts. Bit operations is one of those areas, sadly, which is a shame because you can do a lot of really cool stuff. Perhaps more suited to systems level programming, which I consider game development, but definitely useful stuff to know.
Enough with the pointless rambling. On to the quick lecture!
Bits
The most fundamental item of storage in a binary computer is a single bit. A bit can be in one of two states at any one time, off or on. Off and on are often also referred to as 0 and 1 respectively.
Logic gates
You can perform basic operations on bits by using logic gates. A logic gate is a thing that takes a number of inputs, performs some operation on those inputs and provides an output (which could be used as an input to other logic gates, to switch a light or motor on, or whatever you choose to do with the output). All non-trivial, digital hardware contains millions of logic gates, where each logic gate is constructed from transistors, all arranged in specific configurations, to perform the required tasks. It's a fairly huge leap to go from a single logic gate to a complete CPU, so we'll take things slowly

.
For our purposes we are going to concern ourselves with 4 logic gates. There are others, but they are variations on combinations of these basic four, so we'll ignore them for now. So, our 4 logic gates are AND, OR, NOT and XOR (I'll use the convention that logic gates are all caps to avoid confusing "AND" with "and", etc).
One thing that makes digital logic easier to learn is the concept of a truth table. A truth table is a way of describing how a system works by examining its inputs and outputs, so we'll introduce each gate, show its symbol, explain in English what it does and present a truth table that should help your understanding.
-- AND --
The AND gate requires both of its inputs to be 1 for its output to be 1. In all other states its output will be zero.
Truth table for AND
A | B || C
0 | 0 || 0
1 | 0 || 0
0 | 1 || 0
1 | 1 || 1
The truth table here is saying, for each row, what the inputs (A, B) are and what the output (C) is for those inputs.
-- OR --
The OR gate requires either of its inputs to be 1 for its output to be 1. The output is also 1 in the case where both inputs are 1.
A | B || C
0 | 0 || 0
1 | 0 || 1
0 | 1 || 1
1 | 1 || 1
-- NOT --
The NOT gate inverts the input, outputting a 1 when a 0 is input and vice versa. Unlike the other gates described, NOT gates have one input.
A || C
0 || 1
1 || 0
-- XOR --
The XOR gate is related to the OR gate. It too requires either of its inputs to be 1 for the output to be 1, but in the case where both inputs are 1 its output is 0.
A | B || C
0 | 0 || 0
1 | 0 || 1
0 | 1 || 1
1 | 1 || 0
Binary arithmetic and powers of two
There are 10 types of people in the world; those who understand binary and those who don't. Taking a slight detour from logic gates, we'll look at simple binary arithmetic, then we'll come back to the logic gates and do some math with them (no, really!

). Going back to bits and how they can be off or on, that's not a huge amount of use on its own, but it becomes a lot more useful when you group a bunch of bits together. It's now standard practice to call a group of 8 bits a byte, for example. The older generation of programmers and electronic engineers call a group of 4 bits a nibble or nybble. We won't get too far ahead of ourselves, so we'll look at a group of 3 bits (which, as far as I know, doesn't have any particular name).
So, what can we do with our two bits? Well, each bit can be independantly switched off or on, so I'll write out the various combinations first
0 0 0
0 0 1
0 1 0
0 1 1
1 0 0
1 0 1
1 1 0
1 1 1
Ok, we have 8 different combinations. It's still not massively useful in itself, so we'll assign decimal values to each bit. We'll call the rightmost bit, bit zero. The next left bit will be bit one. The leftmost bit will be bit two. If we had more bits then they'd be bits 3, 4, 5, etc. Starting from the rightmost bit and working left, we'll assign each bit a value that is equal to 2^bitnumber (remember n^0 = 1). e.g.
bit number 2 1 0
value 4 2 1
So, bit zero is "worth" one, bit one is "worth" two and bit two is "worth" four. Now things start to get more useful, because we now have the ability to count in binary. All we need to do is see which bits are set to 1 and we add up the decimal values of all the set bits. For example, given the three bits 1 0 1, we know that the rightmost bit is worth 1, the leftmost bit is worth 4 and the middle bit is off, so we don't count it. Adding those values up gives us the answer, 5. So binary 101 is decimal 5. You should now be able to go through the binary table I drew up earlier and figure out the decimal values for each binary value. I'll do that as an example here
000 = 0
001 = 1
010 = 2
011 = 3
100 = 4
101 = 5
110 = 6
111 = 7
So you can see that with 3 bits we can represent the decimal numbers zero to seven. As you add more bits on the left the range of values you can represent increases. More specifically, given n bits you can represent values from 0 to 2^n-1. So, a byte can represent values between 0 and 255, and so on. Negative numbers are possible, but a trickier proposition; if you're interested and want some homework then look into "two's complement" representation.
So now you know how integer values are stored in the computer. Even though you declare an "int" in C/C++, it's all just a bunch of bits.
More complex logic systems
As I said a little earlier, logic gates can be connected together to construct more complex systems. One of these complex systems is called an adder, and it does as the name suggests, it adds. Specifically it adds two binary numbers. Rather than me badly describe it here it's probably better if you refer to this [http://en.wikipedia.org/wiki/Adder_%28electronics%29] Wikipedia page (if you're interested). You don't really need to fully understand this, but you do need to be convinced that numbers held in your computer are held in a binary representation.
Ok, so now we know that numbers are held in binary we can start to do some funky things with the bit operations in C/C++. If you refer to my solution to the even/odd problem you can see that the line that did the work was
if ( iNum & 1 )
so what's this line doing? Imagine iNum is actually a 3 bit value rather than 32 bit for a second. What I'm doing here is asking the computer to do a bitwise AND between the value in iNum and the binary value 001 (because decimal 1 is binary 001). This has the effect of returning a value which only has bits set where there were bits set in both of the input values. Because one of the input values has just the zero'th bit set (1) then it stands to reason that the only case where the result of this could be anything other than zero is where iNum has its zero'th bit set too. If you refer back to the binary arithmetic we did earlier you'll see that the only odd bit is the zero'th bit - all other bits have even values, and so we have an easy way of determining if a number is even or odd.
But the fun doesn't end there. We actually have a bunch of other useful bit operators we can do stuff with. Cast your mind back to the logic gates from earlier. What would we do if we wanted to OR two values together? Well, C++ has a bitwise OR operator "|". So, what happens when we do, for example, (iNum | 1)? Well, we'll get a result which is whatever is in iNum, but with the zero'th bit set. So you can take any value and make it an odd number equal to or one greater than the original value. C++ also has operators for XOR (^) and NOT (~), should you want to use them.
Two other operators I'd like to cover are the bit shifting operators. Unfortunately the stream in/out operators use the same symbols as the bit shift operators, << and >>, but they act completely differently. Lets say you do the following,
int x = 3;
x = x << 1;
What you are saying here is that you want the bits in x to be shifted left by one place. Likewise you can do
int x = 8;
x = x >> 1;
And here the bits will be shifted to the right by one place. So what happens to the bits? Let's look at the first example,
int x = 3;
x = x << 1;
I'll write the binary values as 4 digits, so 3 would be 0011. If we move each bit in the value to the left by one place we get the binary value 0110. As we shift bits across we shift in unset bits. So, for the second example we start with 1000, shift it right one place and end up with 0100.
How is that useful? Well, it's good for a bunch of things, but one thing to notice is that what you are doing is replacing each set bit (2^bit) by a new value (2^(bit+num_shift_bits)). If you're still none the wiser then don't worry, it's not particularly obvious, but basically you are multiplying and dividing the values by powers of two. Shifting left is equivalent to multiplying by a power of two, shifting right is equivalent to dividing by a power of two. The actual value you are multiplying or dividing by is a function of the number of bits that you are shifting, 2^num_bits. So shifting left by one bit is a multiply by two. Shifting right by 3 bits (2^3 = 8 ) is dividing the value by 8.
I'll wrap things up there I think. There's more to learn, as I said, negative numbers are just one area to look into, but I hope this has been useful and not too boring for you. Comments are more than welcome and I'll try to answer any questions about this stuff if anyone needs further help.
[edit] Apologies for the ridiculous length of this post!
