The number systems give us different ways to represent numbers. For example, we use the decimal system to represent numbers, using the digits from 0 to 9. The systems that are interesting for us, in programming, are the decimal(dec), binary(bin) and hexadecimal(hex).
Before we go deeper into that, let's first see their common characteristics.
A base of a number system (or notation) is the number of symbols that we use to represent the numbers. These symbols (digits) are often called alphabet of that system. The notations are usually named after their base – binary, octal, decimal, hexadecimal. For example the decimal system uses 10 digits.
The base serves not only for naming. More importantly, it plays a role for the representation of the numbers in the positional notations.
When a number is written, its base should be written after the number in subscript.
12310, 1100102, 1F416
If the base is not provided, the default is that the number is in decimal.
In positional notations the value of a digit depends on its position. One example:
10 – the digit 1 has a value of ten
1000 – the same digit 1, has a value of a thousand, because it is in a different position.
In non-positional numeral systems, the value of the digits does not depend on their position. An example for non positional notation is the Roman numeric system. Here is an example:
X : the symbol 'X' means 10
XXX : each symbol has the value of 10. The number “XXX” is equal to their sum 30.
LX : L=50, X=10, so the number LX=60.
In programming, we are interested in positional systems. In particular the most useful once have a base that is an exact power of 2 – binary(base 2) and hexadecimal(base 16 = 2 to the power of 4).
All positional systems that we use, represent the numbers in the same manner. To represent a given number we need to break it down to a sum of exact powers of the base of that notation. This sounds more complicated than it is. You already know how this works, because you work with numbers all the time. Here is an example:
The number 1234 = 1000 + 200 + 30 + 4, where
1000 = 1 * 103 or in words 1 times 10 to the power of 3
200 = 2 * 102 or .. 2 times ten to the power of 2
30 = 3 * 101 or .. 3 times 10 to the power of 1
4 = 4 *100 or .. 4 times 10 to the power of 0. Remember, each number to the power of 0 is equal to 1. So 4 = 4 * 1 = 4 * 100
1234 = 1 * 103 + 2 * 102 + 3 * 101 + 4 * 100
We work with powers of 10, because 10 is the base of the system that we use. The same rule applies to any other positional number system. We always start from a power of 0, as the right-most position and increase the power by one with each position to the left.
The binary number system (or base 2 numeral system) has two digits in its alphabet – 0 and 1. This is the natural “language” for our computers. This is because it is easy to make a stable representation of two different states, using electricity. For instance – no electricity means 0 and the presence of electricity means 1. Usually this representation is implemented by voltage presence. In most computers/microchips the standard voltage for 1 is 5 volts and the standard voltage for 0 is 0 volts.
In the past many variations existed, including negative zero (which is still in use today, but not as much as a 'zero' zero) or even 3-stated machines. However, the 3 stated machines are more difficult to build and not as stable as the binary.
To represent a number in binary you need to follow the universal rule: break down the number to a sum of exact powers of 2.
100 = 64 + 32 + 4
64 = 1 * 26
32 = 1 * 25
4 = 1 * 22
10010 = 1 * 26 + 1 * 25 + 0 * 24 + 0 * 23 + 1 * 22 + 0 * 21 + 0 * 20
10010 = 11001002
We don't use binary in our code. We need to understand the binary numbers in order to work with bitwise operators.
Today the octal numeral system has limited use. Several decades ago it was used in computing and programming. It was convenient representation for systems that had 12-24-36 bit machine words. Why? The octal digits are from 0 to 7. They are 3 bits long. That made it convenient to represent the above mentioned machine words, that are multiple of 3.
For the same reason, with today computers we use the hexadecimal system.
The C programming language makes it easy to use the octal system. To represent an octal value, all you need to do is precede the value with a zero. For instance, the following are examples of using octal literals in C:
Of course, you cannot use the digits 8 and 9 in this format. The following will result in an error:
The hexadecimal number system uses 16 digits. It includes all decimal digits from 0 to 9 and adds the first 6 letters from the English alphabet. So all symbols in hex are: 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F where A(hex) = 10(dec), B = 11, C = 12, D = 13, E = 14 and F = 15. There is no rule if the letters A..F are uppercase or lowercase.
To represent a number as a hex in the source code use the “0x”(zero x) prefix. This tells the compiler that the number is in hex. The number will be converted to its decimal representation before compilation. It doesn't matter if you use decimal or hexadecimal representation of the numbers in your code - to the computer they are the same numbers.
Number representation is done in the same manner, just like with decimal and binary. The difference, of course, is that here we use a base of 16.
10010 = ?(hex)
100 = 6 * 16 + 4 = 6 * 161 + 4 * 160
10010 = 6416
You should already be familiar with that. Let's make one final example:
50010 = 256 + 240 + 4 = 1 * 162 + 15 * 161 + 4 * 160 = 1F416
Now you should understand that those two lines of code are identical:
Here is a small table for fast conversion between decimal, binary and hexadecimal number systems for the first 16 numbers 0..15:
I prepared online converters between the different number systems:
Each page also contains an explanation of the conversion algorithm and example implementation in C. You can freely download and test the source code of the examples. Or you can follow the instructions and create your own version and use the online conversion tool to test if your implementation works correctly.