Bitwise Operators


Introduction

Many struggle with bitwise operators. I must admit I experienced difficulty at first; however, I soon realised that it wasn't the information that was incomprehensible, but more of the way it was presented. Consequently, I decided to create my own tutorial on the subject.

Our ancestors, who first walked this earth, communicated in only a basic language. Computers communicate in a similar fashion known variously as machine language. This language, also known as binary, consists of two digits: 0 and 1. It is possible, albeit irksome, to translate machine language into plausible information.

The following illustrates a basic composition of the binary language:

A single binary digit is known as a bit; collectively, bits.

A byte is comprised of 8 bits.

A short word, or word, is comprised of 16 bits (2 bytes).

A long word, or dword (double word), is comprised of 32 bits (4 bytes, 2 words).

Obviously, everything performed on a computer can be expressed in binary notation. Consider the simple arithmetic of 1 + 1. We visualise the two arguements (1 and 1) and their operator (+); a computer, however, does not. A computer must first convert this alien information into something it can comprehend. Only then can it proceed to perform the actual calculation. A computer's perception of this calculation could be better shown as:

	0011 0001 + 0011 0001

In other words, 0011 0001 is the binary equivalent of the number 1.

The architecture of a computer itself is fundamental in how values are expressed. Most modern processors come in two flavours: 32 and 64 bit. The following displays an 8, 32 and 64 bit binary expression of the number 1.

Bit Binary Equivalent
8 0011 0001
32 0000 0000 0000 0000 0000 0000 0011 0001
64 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0011 0001

From the above, it is apparent that our 8 bit binary equivalent is still present in the 32 and 64 bit counterparts. The original 8 bit binary equivalent appears shifted from left to right. Refer to the Shift bitwise operators mentioned later in this document for a more detailed explanation.

Because binary consists of only two digits, obviously, expressing even a paltry amount of information would yield a lengthy result. Considering that the 8 bit binary equivalent of the number 1 is 0011 0001, imagine how verbose, for example, an entire sentence would be. Fortunately, there is a solution.

Top of Page

Hexadecimal

Hexadecimal could be described as a medium to binary and decimal notation. Hexadecimal utilises 16 characters when expressing a value: 0-F. Conveniently, 4 bits (half a byte) can be represented by one hexadecimal character. Thus, we may use two hexadecimal characters to represent one byte.

A group of 4 bits is known as a nibble. The following table illustrates all possible 4 bit binary combinations and their respective hexadecimal equivalents:

Nibble Hexadecimal Equivalent
0000 0
0001 1
0010 2
0011 3
0100 4
0101 5
0110 6
0111 7
1000 8
1001 9
1010 A
1011 B
1100 C
1101 D
1110 E
1111 F

Considering the above, it's now easier to convert a binary value to its hexadecimal equivalent and vice versa:

	Number 1, in 8 bit binary, as stated, is 0011 0001
	Now, matching each 4 bit portion (nibble) of this value from the table above, we note:

	0011 == 3
	0001 == 1

	Thus, 0011 0001 == 31

Obviously, there must be a convention used for seperating a decimal value from its hexadecimal equivalent, otherwise we wouldn't be able to differentiate between the two. C++ adopts the convention of prefixing a hexadecimal value with a 0x. Thus, 31, in hexadecimal, would be expressed as 0x31. In shorthand, we say:

	0011 0001 == 0x31

Augmenting further, consider the following:



Top of Page

Bitwise Operators

Bitwise operators work on their arguements at the bit (binary) level; hence bitwise. Bitwise operations are considered to be low-level programming, meaning roughly that you probably won't encounter them too often; nevertheless, they are important. A good programmer is one who has a thorough understanding of all facets of the language in question.

There are six bitwise operators in C++:

Bitwise Operator C++ Symbol
NOT ~
AND &
OR |
XOR ^
Left Shift <<
Right Shift >>

Top of Page

Potential

As aforementioned, everything on a computer can be expressed in binary. Processing takes a certain amount of time, memory and patience. Even with today's fast processors, we should save resources where possible. Bitwise operations can, potentially, save a lot of strife.

There are many simple equations in our world; for example: you're either male or female. Similarly, there are many simple equations for a computer. A condition could be contracted to either true (1), or false (0). With this idea in mind, we may use bitwise operators to, potentially, store large amounts of data in small bit structures. Moreover, there's no penalty paid for using bitwise operators, they are extremely fast.

Top of Page

The NOT Operator (~)

The not operator, also known as the ones' complement or inversion operator, is the easiest of all bitwise operators to understand. The NOT operator simply inverts a bit from 0 to 1 and vice versa; for example:

	~ 0101
	------
	  1010
Top of Page

The AND Operator (&)

The AND operator compares two values and returns a value utilising the AND truth table. The AND truth table is as follows:

1 & 1 == 1
1 & 0 == 0
0 & 1 == 0
0 & 0 == 0

The methodology behind the AND operator is akin to simple arithmetic; consider:

	  123
	+ 456
	-----
	  579

In the calculation above, we work from top to bottom, left to right, and compare each of the two arguements against the operator (+) in order to calculate the subsequent 579. A bitwise AND calculation is as follows, and the corelation is quite obvious:

	  1100
	& 1010
	------
	  1000

As with our simple arithmetic, we begin by working from top to bottom, left to right, except in this instance we are comparing each of the two arguements against the AND truth table, as opposed to performing addition. Referring to the AND truth table, we note:

	  1100
	& 1010
	------
	  1000

	In bit 1: 1 & 1 == 1
	In bit 2: 1 & 0 == 0
	In bit 3: 0 & 1 == 0
	In bit 4: 0 & 0 == 0

Yielding 1000. In shorthand, we say:

	1001 & 1100 == 1000
Top of Page

The OR Operator (|)

The OR operator compares two values and returns a value utilising the OR truth table. The OR truth table is as follows:

1 | 1 == 1
1 | 0 == 1
0 | 1 == 1
0 | 0 == 0

When considering the AND and OR bitwise operators, the similarity to the short-circuit logical operators, && and ||, is apparent. Consider the following:

	1 & 1 == 1
	1 & 0 == 0
	0 & 1 == 0
	0 & 0 == 0

	true  && true  == true
	true  && false == false
	false && true  == false
	false && false == false

	1 | 1 == 1
	1 | 0 == 1
	0 | 1 == 1
	0 | 0 == 0

	true  || true  == true
	true  || false == true
	false || true  == true
	false || false == false

The boolean constants false and true are the integer equivalents of 0 and 1 respectively; thank you, George Boole.

Top of Page

The XOR Operator (^)

The XOR operator is also known as the exclusive OR, or OR else operator. Considering that exclusive means incompatible, or limited; the XOR truth table becomes intelligible:

1 ^ 1 == 0
1 ^ 0 == 1
0 ^ 1 == 1
0 ^ 0 == 0

Top of Page

The Left Shift (<<) and Right Shift (>>) Operators

The Shift operators simply move our binary expressions of values the specified number of places to the left, or right. For example:

	1001 << 2
	----
	0100

Any bit a Shift operator creates will be clear. To 0 a bit is also known as clearing. To 1 a bit is also known as setting.

Note that the Shift operators are not technically bitwise operators, as they don't operate on pairs of corresponding bits.

Top of Page

Usage

What possible use could you have for bitwise operators? Well, you'd be surprised; for example:

Perhaps we wish to differentiate between odd and even numbers. The bitwise AND operator is an excellent tool in this instance. Let us first examine the 8 bit binary expressions of numbers, say, from 0 to 5:

Number 8 Bit Binary Equivalent
0 0011 0000
1 0011 0001
2 0011 0010
3 0011 0011
4 0011 0100
5 0011 0101

As we note from the above, the highlighted bit in each expression clearly identifies a pattern. Each bit is either set, or cleared, in each instance. Thus, we may use the AND operator for validation purposes. Consider the following C++ code:

	for (int i = 0; i <= 5; i++)
	{
		if (i & 0x01) cout << i << " is an odd number"  << endl;
		else          cout << i << " is an even number" << endl;
	}

We employ the hexadecimal equivalent of 8 bit binary 0000 0001, 0x01, to validate whether the corresponding bit is either set, or clear. This method of segregating individual bits for clarity is also known as bit masking.

Top of Page

Conclusion

I do hope I have achieved something in this document. There is a whole myriad of information available on the Internet regarding this subject. Where you journey from here is, of course, entirely up to you.