C.S. Basics: From Decimal to Binary And Back Again
Andrew (he/him) ・31 min read
or, Understanding Binary and Hexadecimal Numbers
Binary and hexadecimal numbers can be daunting for beginner computer science students, or anyone unfamiliar with the inner workings of a computer. News reports and viral YouTube videos sometimes use numbers in these different bases to scare or intimidate people, suggesting that the content is over their heads or requires advanced hacking skills™ to understand.
But binary and hexadecimal numbers are easy if you have a good foundation to build on. The easiest way to understand binary numbers is by starting with what you already know about the familiar decimal (base10) numbers and building from there. To begin, let's start somewhere really easy: tally marks.
Tally Marks and Roman Numerals
If you're keeping track of a number which is slowly increasing, or you need to take a quick informal count of somthing like raisedhand votes, you might use tally marks. This primitive counting system involves simply marking a slash to count one instance of a particular thing. Two slashes means two instances, etc.:
 = 1
 = 2
 = 3
 = 4
...when we get to five, we sometimes group these slashes so they're easier to count. Instead of 
for seven, we might write 

. This makes the groups of marks easier to add up in large amounts, but we still need a single tally for each count, whether vertical or horizontal.
Tally marks are a unary (pronounced: younairee) numbering system, a word which comes from the Latin unus, meaning one, because there is only a single symbol  the tally. Five tally marks means five, sixhundred and seventy tally marks means sixhundred and seventy. Obviously, tally marks can get pretty cumbersome pretty quickly.
A slightly easier numbering scheme is the Roman numeral system. This is the one that's used to number Superb Owls, Grand Theft Auto games, and Star Wars movies. In the Roman numeral system, the following figures on the left stand for the respective amounts on the right:
I = 1
II = 2
III = 3
IIII = 4
...that's easy, right? They look a bit like tally marks, where each tally increases the value of the number by one.
At some point, a very intelligent ancient person realised that you could use alternate symbols to denote bigger quantities. Instead of five tally marks (IIIII
, or with a horizontal slash through them), we could use IIII
V
as a standin. Instead of two V
s, we could use an X
to denote ten tally marks. Our list of numbers can then be expanded:
V = 5
VI = 6
VII = 7
VIII = 8
VIIII = 9
X = 10
The ancients realised that IIII
and VIIII
could be confusing to read at a glance. The human brain can only hold about 35 symbols or ideas in working memory at once, so grouping that many symbols together visually is a challenge. This led to the development of subtractive notation in the Roman numeral system, where a smaller number immediately before a larger one means that the smaller number is subtracted from the larger one. So IIII
instead becomes IV
(5  1 = 4
), VIIII
becomes IX
(10  1 = 9
), and so on. The improved numbering system is then:
I = 1 6 = VI
II = 2 7 = VII
III = 3 8 = VIII
IV = 4 9 = IX
V = 5 10 = X
More symbols are introduced for fifty (L
), one hundred (C
), five hundred (D
), and one thousand (M
). As Roman numerals were used for everyday counting, numbers larger than a few thousand were uncommon and there is no single agreedupon way that to write numbers like two or five or tenthousand, and so on. An extended table of Roman numerals might look like:
I = 1 XI = 11 ... ...
II = 2 XII = 12 XLII = 42 XCII = 92
III = 3 XIII = 13 XLIII = 43 XCIII = 93
IV = 4 XIV = 14 XLIV = 44 XCIV = 94
V = 5 XV = 15 XLV = 45 XCV = 95
VI = 6 XVI = 16 XLVI = 46 XCVI = 96
VII = 7 XVII = 17 XLVII = 47 XCVII = 97
VIII = 8 XVIII = 18 XLVIII = 48 XCVIII = 98
IX = 9 XIX = 19 XLIX = 49 XCIX = 99
X = 10 XX = 20 L = 50 C = 100
Roman numerals are an alphabetical numbering system and each number always stands for a specified amount. So each X
in XXX
means 10
(for a total of 10 + 10 + 10 = 30
). This is in contrast to our familiar decimal system where the "5
" in "50
" has a different meaning (five tens) than the "5
" in "5000
" (five thousands), but we'll come back to this in a bit.
Roman numerals are fine for most daytoday uses. They're easy to add  just group together all similar symbols and "compress" every five I
symbols into one V
, then "compress" every two V
s into one X
, and so on:
XXXVII + LXIII (= 37 + 63)
= L + XXXX + V + IIIII
= L + XXXX + VV
= L + XXXXX
= L + L
= C (= 100)
(This is a bit more difficult with symbols like IV
and XC
, which first need to be "expanded" to IIII
and LXXXX
, respectively.) They're easy to subtract, too  just remove all symbols from the minuend (the number being subtracted from) which appear in the subtrahend (the number being subtracted):
XLI  XXVIII (= 41  28)
= XXXXI  XXVIII (first, "expand" `XL` to `XXXX`)
= XX  VII
...and expand any factors in the minuend as necessary
= XVIIIII  VII
= X III
= XIII (= 13)
Roman numerals quickly become overwhelming when dealing with large numbers, or trying work with fractional amounts (which are broken into twelfths rather than tenths like our familiar decimal system). All in all, useful, but not great. Surely there's a better way?
Positional Numbering and Arabic Numerals
There is no single, correct way to represent a number. Whichever system is most useful and easiest to work with will naturally become more popular than the others. In the Middle Ages, a positional numbering system was invented in India and spread west and east by Arabic scholars, eventually becoming popular in Europe, China, and Russia. This system, now known as the HinduArabic numeral system (aka. the Arabic numeral system or just "Western" numerals), is the one you're probably the most familiar with.
In the Arabic numeral system, there are ten distinct figures which represent different quantities. This makes Arabic numerals a decimal numbering system, from the Latin decimus, meaning "one tenth". The ten figures of the Arabic numbering system  with their English names  are (compared to Roman numerals and tally marks):
0 ("zero") = =
1 ("one") = I = 
2 ("two") = II = 
3 ("three") = III = 
4 ("four") = IV = 
5 ("five") = V = 
6 ("six") = VI =  
7 ("seven") = VII =  
8 ("eight") = VIII =  
9 ("nine") = IX =  
The most critical one is 0
, which represents a lack of any tally marks. It may not seem like it to you, but creating a symbol for "nothing" is a huge conceptual leap. If you're used to using numerals for counting, why would you ever need a symbol for "nothing"? Doesn't the lack of symbols represent "nothing"?
The Arabic numeral system uses this 0
figure in an extremely clever way, as well. In the Roman numeral system, if we want to write three ones (ie. the number three), we need to write the figure for one three times (III
)? and if we want to write three tens (ie. the number thirty), we need to write the figure for ten three times (XXX
)? The same applies to three hundred (CCC
) and three thousand (MMM
)! We need a new figure (I
, X
, C
, M
) for every power of ten. And when we run out of figures, we run out of numbers.
Instead of doing that, the Arabic system uses what's known as a positional numbering system, in that the position of a given figure within the expression for a number determines its value. so if we want three ones, we can use the figure 3
in a particular position in a number (the ones' position); if we want three hundreds, we use the same figure in a different position in the same number (the hundreds' position). We can use 0
as a placeholder to show what position the different figures are in.
Because Arabic numbers are a base10 (decimal) system, as well as a positional system, each position in a given number is 10
times as large as the position before it. The rightmost position is the ones' position, and to the left of that is the tens' position, then the hundreds' position, and so on:
123

____(3)__ ones = (3 * 10^0) = (3 * 1) = 3

___(2)____ tens = (2 * 10^1) = (2 * 10) = 20

__(1)__ hundreds = (1 * 10^2) = (1 * 100) = 100
The number 123
translates to 1
(one) hundred plus 2
(two) tens plus 3
(three) ones. If we subtract three ones from this number, we need to use the placeholder 0
(zero):
123  3 = 120
Otherwise, if we simply dropped the ones' place (if we had no symbol for "nothing"), we would have the number 12
, which is quite different from 120
. The Arabic numeral system ingeniously uses this positional system, plus the figure 0
, to denote any "counting" number imaginable (the positive integers and zero), in a compact form, with only ten distinct figures.
Addition and Subtraction
Addition and subtraction is also greatly improved with positional numbering systems. Instead of grouping together like figures like we had to do with Roman numerals, we can simply subtract the numbers positionbyposition:
345
 23

322
We subtract the ones' place from the ones' place, the tens' place from the tens' place, and so on. If a figure is missing in a certain position, we assume it to be 0
(so 23
above becomes 023
). But, similar to how we had to "compress" and "expand" numbers in the Roman numeral system, we also must do that with Arabic numerals:
234 Here, 5 is larger than 4, so we need to "expand" one
 35 of the tens into ten ones. To make this calculation easier,
 I'll put each "place" into its own "box":
[ 2][ 3][ 4] We "trade" one of the tens in the minuend for ten
 [ 0][ 3][ 5] ones, turning 4 in the ones' place into 14

[ 2][ 2][14] Now, we can easily subtract 5 from 14 to get 9.
 [ 0][ 3][ 5] But we have a similar problem with the tens' place.

[ 1][12][14] "Expand" the 2 in the hundreds' place into ten tens
 [ 0][ 3][ 5] in the tens' place, turning 2 tens into 12 tens

[ 1][12][14] Finally, we can subtract the numbers in each
 [ 0][ 3][ 5] position without any more "expansions".

[ 1][ 9][ 9] => 23435=199
This process is likely so ingrained in you that you don't even realise you're doing it. To better explore what is happening in the example above, let's build an addition table for Arabic numerals.
Addition and Multiplication in Decimal
Below, the leftmost number in each row after the first is added to the number at the top of each column to get the number in each cell. The first row is easy  0
plus any figure equals that original figure (x
tally marks plus 0
(no) tally marks equals x
tally marks):
+  0  1  2  3  4  5  6  7  8  9 

0  0  1  2  3  4  5  6  7  8  9 
A subtraction table would look exactly the same for this first row, because any number minus 0
is that same number. We don't get much insight from a subtraction table, so we'll forgo that for now. The second row is slightly more complex, adding 1
to a figure increments it to the next figure in the sequence:
+  0  1  2  3  4  5  6  7  8  9 

0  0  1  2  3  4  5  6  7  8  9 
1  1  2  3  4  5  6  7  8  9  ? 
Note: subtracting 1 from a number is called decrementing.
...so 1+2=3
, 1+4=5
and so on. But what comes after 9
? We have no special symbol for ten ones, like we did in the Roman system. Instead, we use 0
as a placeholder in the ones' place, and put a 1
(one) in the tens' place to create the number 10
 the result of our decimal positional number system. One more than nine ones is ten ones or one ten:
+  0  1  2  3  4  5  6  7  8  9 

0  0  1  2  3  4  5  6  7  8  9 
1  1  2  3  4  5  6  7  8  9  10 
Adding 2
to a number increments it twice, advancing it by two figures:
+  0  1  2  3  4  5  6  7  8  9 

0  0  1  2  3  4  5  6  7  8  9 
1  1  2  3  4  5  6  7  8  9  10 
2  2  3  4  5  6  7  8  9  10  11 
Let's fill in the rest of the addition table:
+  0  1  2  3  4  5  6  7  8  9 

0  0  1  2  3  4  5  6  7  8  9 
1  1  2  3  4  5  6  7  8  9  10 
2  2  3  4  5  6  7  8  9  10  11 
3  3  4  5  6  7  8  9  10  11  12 
4  4  5  6  7  8  9  10  11  12  13 
5  5  6  7  8  9  10  11  12  13  14 
6  6  7  8  9  10  11  12  13  14  15 
7  7  8  9  10  11  12  13  14  15  16 
8  8  9  10  11  12  13  14  15  16  17 
9  9  10  11  12  13  14  15  16  17  18 
You can see above that when we run out of symbols (ie. when we need a number greater than 9
), we make use of our positional numbering system, incrementing the next position from 0
(which is implied) to 1
, and reducing the current position from 9
back to 0
. We go through the same incrementing over and over until we reach 19
, where, when we try to increment again, the tens' place will be incremented from 1
to 2
, but the ones' place will jump from 9
back to 0
. So the number which follows 19
is 20
.
You can think of these "positions" like gears. We have to ratchet the "ones' place gear" through nine notches, and on the tenth notch, the nextbiggest gear is incremented (the tens' place gear) one notch, while the current gear (the ones' place gear) has made a full turn and is back to where it started  at
0
.
Similarly, we can build a multiplication table. The first two rows are easy as 0
times any number is 0
, and 1
times any number is that same number:
*  0  1  2  3  4  5  6  7  8  9 

0  0  0  0  0  0  0  0  0  0  0 
1  0  1  2  3  4  5  6  7  8  9 
Multiplying a number by 2
is the same as adding that number to zero twice (2
times). So 2 x 1
(or 2 * 1
) is the same as 0 + 1 + 1
, which we already know from our addition table is 2
. Similarly, 2 * 2 = 0 + 2 + 2 = 4
, and 2 * 3 = 0 + 3 + 3 = 6
. This pattern continues down the diagonal of the addition table, giving us the second row of the multiplication table. To avoid overexplaining this whole thing (with which you're probably already very familiar), here's the multiplication table in full for an Arabicnumeral based decimal system:
*  0  1  2  3  4  5  6  7  8  9 

0  0  0  0  0  0  0  0  0  0  0 
1  0  1  2  3  4  5  6  7  8  9 
2  0  2  4  6  8  10  12  14  16  18 
3  0  3  6  9  12  15  18  21  24  27 
4  0  4  8  12  16  20  24  28  32  36 
5  0  5  10  15  20  25  30  35  40  45 
6  0  6  12  18  24  30  36  42  48  54 
7  0  7  14  21  28  35  42  49  56  63 
8  0  8  16  24  32  40  48  56  64  72 
9  0  9  18  27  36  45  54  63  72  81 
Note that the numbers on the diagonal of this table are the squares of the nonnegative integers (0^2 = 0 * 0 = 0
, 1^2 = 1
, 2^2 = 4
, 3^3 = 9
, ...). This surely is all secondnature to you, but if you understand how these numbers are constructed, then you'll have a much better foundation upon which to understand how binary and hexadecimal and other numbers are constructed.
Ace of Base(s)
We call decimal a base10 numbering system because there are ten unique decimal digits which can be used to construct numbers in the system (0
through 9
). But there is nothing special about using ten symbols. Ancient decimal numbering systems probably got their start from people counting on their fingers (of which, usually, one has ten). But this is not always the case.
Octal (base8)
For istance, the Pamean peoples of Mesoamerica employ a base8 (aka. octal) system. This could arise just as naturally as a base10 system; for example, the indigenous Yuki people of what is now southern California originally employed a similar system, counting on the spaces between the fingers, rather than the fingers themselves.
How would a Pamean count to ten? Well, in an octal number system, one has unique names and symbols for all numbers from zero through seven:
0 = "zero"
1 = "one"
2 = "two"
3 = "three"
4 = "four"
5 = "five"
6 = "six"
7 = "seven"
But, just like we don't have a special symbol for ten in decimal (it's actually a 1
and a 0
next to each other), there would be no special symbol for eight in an octal system. In fact, if this octal numbering system was also positional, like the Arabic numeral system, then adding 1
to 7
should increase the eights' place from 0
to 1
and decrease the ones' place back to 0
, just like it did in decimal. To avoid confusion, from this point on, we'll specify the base in the name of a number, so 8
in decimal would instead be 8 base 10
. The octal number list above can then be expanded:
0 = "zero base eight" 10 = "eight base eight" 20 = "sixteen base eight"
1 = "one base eight" 11 = "nine base eight" 21 = "seventeen base eight"
2 = "two base eight" 12 = "ten base eight" 22 = "eighteen base eight"
3 = "three base eight" 13 = "eleven base eight" 23 = "nineteen base eight"
4 = "four base eight" 14 = "twelve base eight" 24 = "twenty base eight"
5 = "five base eight" 15 = "thirteen base eight" 25 = "twentyone base eight"
6 = "six base eight" 16 = "fourteen base eight" 26 = "twentytwo base eight"
7 = "seven base eight" 17 = "fifteen base eight" 27 = "twentythree base eight"
Notice that no single symbol exists anywhere for "eight", just like no single symbol exists for "ten" in a decimal system. Instead, when we reach 7
in the ones' place, if we wish to increment the number, we increment the eights' place instead, and decrease the ones' place to 0
.
The representation of a number in octal, just like in decimal, has encoded in it a formula for how to calculate it. An analog to the above diagram we made for decimal could, in octal, be:
123 (base 8)

____(3)_____ ones = (3 * 8^0) = (3 * 1) = 3

___(2)_____ eights = (2 * 8^1) = (2 * 8) = 16

__(1)__ sixtyfours = (1 * 8^2) = (1 * 64) = 64
So 123 base eight
does not equal 123 base ten
, but rather 3 + 16 + 64 = 83 base ten
. This makes sense because (all base ten below):
__ "tens' place"
 __ "ones' place"
 
[octal representation]  
(1 * 64) + (2 * 8) + (3 * 1) = (8 * 10) + (3 * 1)
   [decimal representation]
  
  __ "ones' place"
 __ "eights' place"
__ "sixtyfours' place"
An octal addition table would look like:
+  0  1  2  3  4  5  6  7 

0  0  1  2  3  4  5  6  7 
1  1  2  3  4  5  6  7  10 
2  2  3  4  5  6  7  10  11 
3  3  4  5  6  7  10  11  12 
4  4  5  6  7  10  11  12  13 
5  5  6  7  10  11  12  13  14 
6  6  7  10  11  12  13  14  15 
7  7  10  11  12  13  14  15  16 
Note that it looks exactly the same as a decimal addition table, until we get to values larger than 7
. Then, we have to increment the eights' place and set the ones' place back to 0
. An octal multiplication table would look like:
*  0  1  2  3  4  5  6  7 

0  0  0  0  0  0  0  0  0 
1  0  1  2  3  4  5  6  7 
2  0  2  4  6  10  12  14  16 
3  0  3  6  11  14  17  22  25 
4  0  4  10  14  20  24  30  34 
5  0  5  12  17  24  31  36  43 
6  0  6  14  22  30  36  44  52 
7  0  7  16  25  34  43  52  61 
It takes a bit of time to make, especially if you're not familiar with octal numbers, but you can easily check that it's accurate. Take any pair of numbers in the table and get the decimal equivalent of their product (for example 7 base ten
times 6 base ten
equals 42 base ten
) and divide it by eight as many times as you can without fractions (so 42 base ten
divided by 8 base ten
equals 5 base ten
with a remainder of 2 base ten
). The result and the remainder will be the digit in the eights' place and the digit in the ones' place of the octal representation of that number (52 base eight
== 42 base ten
), respectively.
The 4
column has another cool property where every number either ends in 0
or 4
. This is because when you add 4
to 0
in octal, you get 4
, but when you add 4
to 4
, you don't get 8
(because 8
doesn't exist in octal), instead you get 10
. 4
in octal acts the same way as 5
in decimal, in that respect.
There's one more neat thing to notice on this addition table. In grade school you may have learned that when you multiply any number 2
through 9
by 9
(base10) that the result has some interesting properties:
2 * 9 = 18 (all base10)
3 * 9 = 27
4 * 9 = 36
5 * 9 = 45
6 * 9 = 54
7 * 9 = 63
8 * 9 = 72
9 * 9 = 81
The first digit of the result (in the tens' place) is the first number, minus one. And the second digit of the result is the nine's complement of the first digit. In other words, it's the number that you need to add to the first digit of the product for the sum to be 9
:
__ [first digit]
 __ [second digit]
 
x = 2: 2 * 9 = 18 => [x] * 9 = [x1][9(x1)]
x = 3: 3 * 9 = 27 => [x] * 9 = [x1][9(x1)]
x = 4: 4 * 9 = 36 => [x] * 9 = [x1][9(x1)]
x = 5: 5 * 9 = 45 => [x] * 9 = [x1][9(x1)]
x = 6: 6 * 9 = 54 => [x] * 9 = [x1][9(x1)]
x = 7: 7 * 9 = 63 => [x] * 9 = [x1][9(x1)]
x = 8: 8 * 9 = 72 => [x] * 9 = [x1][9(x1)]
x = 9: 9 * 9 = 81 => [x] * 9 = [x1][9(x1)]
The same thing happens in octal, but instead of a nine's complement, we have a seven's complement:
x = 2: 2 * 7 = 16 => [x] * 7 = [x1][7(x1)]
x = 3: 3 * 7 = 25 => [x] * 7 = [x1][7(x1)]
x = 4: 4 * 7 = 34 => [x] * 7 = [x1][7(x1)]
x = 5: 5 * 7 = 43 => [x] * 7 = [x1][7(x1)]
x = 6: 6 * 7 = 52 => [x] * 7 = [x1][7(x1)]
x = 7: 7 * 7 = 61 => [x] * 7 = [x1][7(x1)]
...pretty cool, huh?
Quaternary (base4)
Number systems, as mentioned before, can be in any base. They can be base 20
or 60
or 6.02 * 10^24
. Larger bases require more unique symbols but can represent large numbers more compactly. (If you had a base10,000 system, then you could represent any number from 0
to 9,999
with just a single unique character.) Smaller bases require fewer symbols but even relatively small numbers require us to use those symbols several times, making the numbers "big", in terms of area, when printed on a page.
If you're familiar with how memory is stored within a computer, you may be familiar with the terms bit and byte. A bit is a single binary digit of information, a 0
or a 1
(will come back to this in a second). A byte is composed of eight consecutive bits (also known as an octet of bits). But the definition of a byte hasn't always unambiguously been 8 bits. In the midtolate 1950s, the size of a "byte" depended on who you were talking to, and it could be 8
, 6
, or even 4
bits. Nowadays, four consecutive bits (a "quartet") is also sometimes called a nybble
.
The powers of 2
are very important in computer science, for reasons we will cover when we get to binary. So instead of looking at base7 or base5 numbers, let's jump from base8 down to base4. To see how our method for developing addition and multiplication tables scales down, let's try to construct a quaternary addition table. To start, we won't need any digit larger than 3
(because four will be represented by 10
):
+  0  1  2  3 

0  
1  
2  
3 
We can fill in the trivial row and column, where we add 0
:
+  0  1  2  3 

0  0  1  2  3 
1  1  
2  2  
3  3 
Then, let's fill in any sum which is less than four:
+  0  1  2  3 

0  0  1  2  3 
1  1  2  3  
2  2  3  
3  3 
Now, where the sum is four, we write 10
, because in quaternary, we have a fours' place and a ones' place, so adding 1
to 3
will cause the fours' place to increment, and the ones' place to "roll over" back to 0
:
+  0  1  2  3 

0  0  1  2  3 
1  1  2  3  10 
2  2  3  10  
3  3  10 
If we add 1
to 10
in quaternary, we get 11
; and if we add 1
to 11
, we get 12
:
+  0  1  2  3 

0  0  1  2  3 
1  1  2  3  10 
2  2  3  10  11 
3  3  10  11  12 
Great! Our table is a lot easier to fill in and work with, and a lot smaller now. Let's check that it makes sense. In the last row, we have 3 base 4
+ 2 base 4
= 11 base 4
. Does that check out? Well, 11 base 4
has a 1
in the fours' place and a 1
in the ones' place, so it's equivalent to 5 base ten
, which is 3 base ten
+ 2 base ten
! It seems okay! A multiplication table in quaternary looks like:
*  0  1  2  3 

0  0  0  0  0 
1  0  1  2  3 
2  0  2  10  12 
3  0  3  12  21 
Again, since 2
is half of the base, we get that alternating 0
2
0
2
in the ones' place going down the 2
column, similar to what we had with the 4
column in octal. And we also have a three's complement in the 3
column, though there are only two examples:
x = 2: 2 * 3 = 12 => [x] * 3 = [x1][3(x1)]
x = 3: 3 * 3 = 21 => [x] * 3 = [x1][3(x1)]
So there are fewer symbols and the addition and multiplication tables are easier to work out. But one of the downsides of smaller bases is that numbers require more ink to print, because they require larger numbers of characters. For instance, the number 123 base four
equals only 27 base ten
:
123 (base 4)

____(3)__ ones = (3 * 4^0) = (3 * 1) = 3

___(2)___ fours = (2 * 4^1) = (2 * 4) = 8

__(1)__ sixteens = (1 * 4^2) = (1 * 16) = 16
...and 3 + 8 + 16 = 27 base ten
. The effect is not as noticable for small numbers but it becomes significant for very large numbers. For example, the world population as of this writing is estimated to be about 7.55 billion people. In decimal and in quaternary, those numbers are:
7550000000 (decimal)
13002000331232000 (quaternary)  70% more digits
When writing a number
n
in baseb
, ifn
is large relative tob
, the number of digits required to writen
in that base is inversely proportional toln(b)
. Sinceb
is4
for quaternary, a large number should requireln(10)/ln(4) ~ 1.66x
as many digits in quaternary as in decimal. (source)
Binary (base2)
The smallest useful positional numbering system is base 2 or binary. If we tried to use a unary system, as we did with tally marks, then every "place" becomes a ones' place. A base1 numbering system means that every place only counts for 1
:
111 (base 1) = 3 (base 10)

____(1)__ ones = (1 * 1^0) = (1 * 1) = 1

___(1)____ ones = (1 * 1^1) = (1 * 1) = 1

__(1)______ ones = (1 * 1^2) = (1 * 1) = 1
What's worse is that, with a unary system, we don't have a second symbol (by definition), so we have no 0
character as a placeholder. This makes the unary system...kind of useless, right? It's just tally marks. Instead, if we add that second symbol in (0
) to create a binary system, we can do a bit more. In fact, we can do everything that a computer can do.
In a binary system, we have two symbols: 0
and 1
. When we add 1
to 0
(or vice versa), we get 1
. When we add 1
to 1
, we've already run out of symbols! So, like we've done previously, we increment the next place (which, in this case, is the twos' place) and reduce the ones' place to 0
:
1 + 1 = 10 (binary)
Hence the old joke: there are 10 kinds of people in this world. Those who understand binary, and those who don't.
A binary addition table is extremely simple:
+  0  1 

0  0  1 
1  1  10 
A binary multplication table is even easier, as all the regular rules for multiplying by 1
and 0
apply:
*  0  1 

0  0  0 
1  0  1 
Instead of having a ones' place, a tens' place, a hundreds' place, and so on, a binary number has a one's place, a twos' place, a fours' place and so on, through all the powers of two:
10101 (base 2) = 21 (base 10)

______(1)__ ones = (1 * 2^0) = (1 * 1) = 1

_____(0)____ twos = (0 * 2^1) = (0 * 2) = 0

____(1)_____ fours = (1 * 2^2) = (1 * 4) = 4

___(0)______ eights = (0 * 2^3) = (0 * 8) = 0

__(1)______ sixteens = (1 * 2^4) = (1 * 16) = 16
Note that the binary representation is much less compact than the decimal representation, but that it only requires those two digits  0
and 1
. Binary digits have become so commonplace in the study of computers that a word was coined to shorten the phrase: bit. A bit is a single binary digit  either a 0
or a 1
.
Binary is the "language" of computers. Anything that can be represented in one of two states can be represented by a bit. A switch can be open or closed, on or off; electricity can be flowing through a circuit or not; a magnet can have its positive end pointing up or down. The universe is full of blackandwhite situations which can be represented by a single bit.
Computers were originally built of huge messes of circuits and huge chunks of magnetic materials and vaccum tubes. In those early days, binary code was (and still is) the most natural way to represent information in a complex circuit  electricity either is or is not flowing through this particular wire or transistor or whatever. And although several extremelysuccessful decimal computers were built throughout the 1950s and 60s, shrinking hardware and a host of other factors led to binary beating out decimal in the middle of the past century. To understand computers, you must understand binary.
Hexadecimal (base16)
So far, we've focused on removing symbols from the Arabic numeral system, creating base8, base4 and even base2 number systems. But what if we try adding symbols? What if, instead of base10, we developed a base16 numeral system? A base16 system is a hexadecimal system (from "hexa", six; and "deca", ten).
This time, instead of removing some of our familiar Arabic numerals, we need to add new ones. We need singlecharacter representations of all numbers zero through fifteen:
0 ("zero") = =
1 ("one") = I = 
2 ("two") = II = 
3 ("three") = III = 
4 ("four") = IV = 
5 ("five") = V = 
6 ("six") = VI =  
7 ("seven") = VII =  
8 ("eight") = VIII =  
9 ("nine") = IX =  
? ("ten") = X =  
? ("eleven") = XI =   
? ("twelve") = XII =   
? ("thirteen") = XIII =   
? ("fourteen") = XIV =   
? ("fifteen") = XV =   
So we need six new symbols. Usually, we borrow an idea from the Romans and use alphabetic characters to make up these six remaining symbols, but they really could be anything. You could use emoji or arrows or wingdings or whatever makes you happy. For ease of use, though, let's use the first six letters of the alphabet:
0 ("zero") = =
1 ("one") = I = 
2 ("two") = II = 
3 ("three") = III = 
4 ("four") = IV = 
5 ("five") = V = 
6 ("six") = VI =  
7 ("seven") = VII =  
8 ("eight") = VIII =  
9 ("nine") = IX =  
A ("ten") = X =  
B ("eleven") = XI =   
C ("twelve") = XII =   
D ("thirteen") = XIII =   
E ("fourteen") = XIV =   
F ("fifteen") = XV =   
In this encoding, B
means "eleven". This is just as valid as 11
meaning "eleven" in a base10 system. There is nothing inherently "good" or "bad" about either system. We use different bases when they suit us. Let's look at the hexadecimal addition table, filling in everything up to F
first:
+  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F 

0  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F 
1  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  
2  2  3  4  5  6  7  8  9  A  B  C  D  E  F  
3  3  4  5  6  7  8  9  A  B  C  D  E  F  
4  4  5  6  7  8  9  A  B  C  D  E  F  
5  5  6  7  8  9  A  B  C  D  E  F  
6  6  7  8  9  A  B  C  D  E  F  
7  7  8  9  A  B  C  D  E  F  
8  8  9  A  B  C  D  E  F  
9  9  A  B  C  D  E  F  
A  A  B  C  D  E  F  
B  B  C  D  E  F  
C  C  D  E  F  
D  D  E  F  
E  E  F  
F  F 
...what comes after F
? Well, we do the same thing we did in all the previous bases: increment the next place (in this case, the sixteens' place) and reduce the current place back to 0
. So F + 1 = 10
in hexadecimal:
+  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F 

0  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F 
1  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  10 
2  2  3  4  5  6  7  8  9  A  B  C  D  E  F  10  
3  3  4  5  6  7  8  9  A  B  C  D  E  F  10  
4  4  5  6  7  8  9  A  B  C  D  E  F  10  
5  5  6  7  8  9  A  B  C  D  E  F  10  
6  6  7  8  9  A  B  C  D  E  F  10  
7  7  8  9  A  B  C  D  E  F  10  
8  8  9  A  B  C  D  E  F  10  
9  9  A  B  C  D  E  F  10  
A  A  B  C  D  E  F  10  
B  B  C  D  E  F  10  
C  C  D  E  F  10  
D  D  E  F  10  
E  E  F  10  
F  F  10 
We can continue adding 1
to numbers greater than F
and we don't hit another "rollover" until 1F
, which doesn't happen in the addition table:
+  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F 

0  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F 
1  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  10 
2  2  3  4  5  6  7  8  9  A  B  C  D  E  F  10  11 
3  3  4  5  6  7  8  9  A  B  C  D  E  F  10  11  12 
4  4  5  6  7  8  9  A  B  C  D  E  F  10  11  12  13 
5  5  6  7  8  9  A  B  C  D  E  F  10  11  12  13  14 
6  6  7  8  9  A  B  C  D  E  F  10  11  12  13  14  15 
7  7  8  9  A  B  C  D  E  F  10  11  12  13  14  15  16 
8  8  9  A  B  C  D  E  F  10  11  12  13  14  15  16  17 
9  9  A  B  C  D  E  F  10  11  12  13  14  15  16  17  18 
A  A  B  C  D  E  F  10  11  12  13  14  15  16  17  18  19 
B  B  C  D  E  F  10  11  12  13  14  15  16  17  18  19  1A 
C  C  D  E  F  10  11  12  13  14  15  16  17  18  19  1A  1B 
D  D  E  F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C 
E  E  F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D 
F  F  10  11  12  13  14  15  16  17  18  19  1A  1B  1C  1D  1E 
The multiplication table is a different story, though. It may look like a crazy jumble of letters and numbers, but I repeat, it's just as valid as any other numbering system. It is just unfamiliar to the layperson:
*  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F 

0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
1  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F 
2  0  2  4  6  8  A  C  E  10  12  14  16  18  1A  1C  1E 
3  0  3  6  9  C  F  12  15  18  1B  1E  21  24  27  2A  2D 
4  0  4  8  C  10  14  18  1C  20  24  28  2C  30  34  38  3C 
5  0  5  A  F  14  19  1E  23  28  2D  32  37  3C  41  46  4B 
6  0  6  C  12  18  1E  24  2A  30  36  3C  42  48  4E  54  5A 
7  0  7  E  15  1C  23  2A  31  38  3F  46  4D  54  5B  62  69 
8  0  8  10  18  20  28  30  38  40  48  50  58  60  68  70  78 
9  0  9  12  1B  24  2D  36  3F  48  51  5A  63  6C  75  7E  87 
A  0  A  14  1E  28  32  3C  46  50  5A  64  6E  78  82  8C  96 
B  0  B  16  21  2C  37  42  4D  58  63  6E  79  84  8F  9A  A5 
C  0  C  18  24  30  3C  48  54  60  6C  78  84  90  9C  A8  B4 
D  0  D  1A  27  34  41  4E  5B  68  75  82  8F  9C  A9  B6  C3 
E  0  E  1C  2A  38  46  54  62  70  7E  8C  9A  A8  B6  C4  D2 
F  0  F  1E  2D  3C  4B  5A  69  78  87  96  A5  B4  C3  D2  E1 
You can tell by looking at the 2
column in the above table that A
, C
, and E
are even numbers in hexadecimal. You can also see, in the 8
column, the familiar 8080
behaviour that we noticed with 4
in octal. The F
column also shows (after the 1
row) twodigit numbers where the digits are F's complement to each other ((1 + E) = (2 + D) = ... = F
), as we saw before with decimal and octal. Finally, the numbers on the diagonal of the table above are again the squares of those numbers, so 2^2 = 4
, 3^2 = 9
, and 4^2 = 10
, which, remember, means 1
sixteen and 0
ones in hexadecimal.
There's a real beauty with these bases. The same regular patterns occur whether you work in base10 or base8 or base16. Any of these systems could be useful in particular situations, and in fact they are used by computer scientists very regularly. After base10, the two most common bases in computer science are probably base2 (binary) and base16 (hexadecimal).
Working With Multiple Bases
BaseN
to Decimal
Sometimes, we need to work with numbers in different bases, and we need to be able to convert back and forth from one base to another. It's pretty easy, usually, to convert from a nondecimal base into a decimal base. You just need to remember how each position is defined in a given base:
baseN
[4][1][2][3]
   
   __ ones' place (always)
  _________ N place
 ________ (N^2) place
___________ (N^3) place, etc.
So to convert from baseN
to decimal, you multiply the rightmost digit by 1
; and add that to the next digit, which you multiply by N
; and add that to the next digit, which you multiply by N^2
; ...
baseN to decimal
[4][1][2][3] => 3 + (2 * N) + (1 * N^2) + (4 * N^3) = ...
Obviously, if there's a 4
in the number, then the base must be at least N = 5
(because, remember, there's no symbol for N
in a baseN
system, like there's no symbol for "ten" in a base10 system). Suppose the base above is N = 5
, then:
base5 to decimal
[4][1][2][3] => 3 + (2 * 5) + (1 * 5^2) + (4 * 5^3) = 3 + 10 + 25 + 500 = 538 base ten
Decimal to Binary
Converting from decimal into a particular base is a bit trickier. Suppose we have the number 637 base ten
, and we want to convert it to binary (base2). We must find the largest power of 2
which we can subtract from the original number 637 base 10
where the result of the subtraction is not a negative number. Let's make a short table of powers of 2
:
2^0 = 1
2^1 = 2
2^2 = 4
2^3 = 8
2^4 = 16
2^5 = 32
2^6 = 64
2^7 = 128
2^8 = 256
2^9 = 512
2^10 = 1024
It looks like 2^9
is the largest power of 2 base ten
that we can subtract from 637 base ten
without the result being negative:
637  2^9 = 637  512 = 125
Great! Remember 2^9
and let's keep going. We now have 125 base 10
. The largest power of 2
we can subtract from this is 2^6
:
125  2^6 = 125  64 = 61
Okay! Write down 2^6
as well, and let's continue for a few more terms:
61  2^5 = 61  32 = 29 (remember 2^5)
29  2^4 = 29  16 = 13 (remember 2^4)
13  2^3 = 13  8 = 5 (remember 2^3)
5  2^2 = 5  4 = 1 (remember 2^2)
1  2^0 = 1  1 = 0 (...and 2^0)
Fantastic! The powers we used here give us the positions of the base2 number which are 1
, rather than 0
. In this case, the first position is numbered 0
(for 2^0 = 1
for the ones' place) and the second is numbered 1
(for 2^1 = 2
for the twos' place). So we have 1
at the 0th, 2nd, 3rd, 4th, 5th, 6th, and 9th places, and 0
everywhere else:
[1][0][0][1][1][1][1][1][0][1] = 1001111101 = 637 base ten
Decimal to Octal
For binary, we can only ever have a 1
or a 0
at a given position. We can either subtract a particular power of 2
from a number or we can't (without going negative). It's a bit more difficult with larger bases. Let's try octal, for instance.
Let's convert 637 base ten
to octal. We start by writing the powers of eight (in base10):
8^0 = 1
8^1 = 8
8^2 = 64
8^3 = 512
8^4 = 4096
Then, we subtract the largest one we can from 637 base ten
 512 base ten
(8^3
):
637  512 = 125
From 125 base ten
we can subtract 64 base ten
(8^2
) just once:
125  64 = 61
At this point, we have a different situation than the one we encountered with binary. Here, we can subtract a power of eight multiple times. In fact, we can subtract 8^1
(or just 8
) exactly seven times:
61  (8^1)*7 = 61  56 = 5
Then, we must subtract 1
(8^0
) five times:
5  (8^0)*5 = 5  5 = 0
So in total, we found that 637 base ten
is equal to...
637 = (1 * 8^3) + (1 * 8^2) + (7 * 8^1) + (5 * 8^0)
Doublecheck that the right side of the above equation equals the leftside in base ten. To convert from decimal to octal, we take the multiplicative factors of the powers of eight, above (1
, 1
, 7
, 5
) and arrange them so that the 0th power is the rightmost digit, then the 1st power is to the left of that, and so on:
637 base ten = 1175 base 8
Hexadecimal to Decimal
For web designers, probably the most common nondecimal number system you'll regularly encounter is the hexadecimal system. Hexadecimal codes  or just "hex" codes, for short  are used to describe colors used on the web. For instance, #3CB371
is a blueish, greenish, mossy color; #708090
is a very slightly blueish grey; #D2691E
is an orange/brown color; and so on.
But how do these numbers correspond to the colors they represent?
Well, a hex code is actually three hexadecimal numbers in one:
#3CB371 => [3C] [B3] [71]
#708090 => [70] [80] [90]
#D2691E => [D2] [69] [1E]
A twodigit hexadecimal number has a range from 00
to FF
, where 00
is equivalent to 0 base ten
and FF
means F
sixteens and F
ones. F base 16
is the same as 15 base 10
, so:
hexadecimal: FF base 16 = ( F * 16) + ( F * 1) =
decimal: (15 * 16) + (15 * 1) = 255
So a twodigit hexadecimal number can encode any number between 0 base ten
and 255 base ten
, inclusive. The six digit hex codes above give three numbers on the range 0255 base ten
. Those numbers give the relative "amount" of red, green, and blue in a color:
red green blue red green blue
#3CB371 => [3C] [B3] [71] => [ 60/255] [179/255] [113/255] => [23.53%] [70.20%] [44.31%]
#708090 => [70] [80] [90] => [112/255] [128/255] [144/255] => [43.92%] [50.20%] [56.47%]
#D2691E => [D2] [69] [1E] => [210/255] [105/255] [ 30/255] => [82.35%] [41.18%] [11.76%]
The pixels on your screen are actually three "pixels" in one  there's a small bit of red, a small bit of green, and a small bit of blue. The numbers in a hex code tell each chunk of the pixel how brightly it should shine. It all three are shining at the same strength, the color will appear to be white, black, or a shade of grey in between. When the three colors shine at different relative strengths, we can get almost any color imaginable.
Summary
I hope this was a useful introduction to different bases, how we work with numbers in those bases, and how useful binary and hexadecimal numbers are. Though they may seem scary at first, working with binary and hexadecimal numbers can be as easy as working with decimals  you just have to practice!
dev.to/you
Claim your page on DEV before someone else does
Join DEV Now
Open source
Free forever
Level up every day
🤝
Very good explained! I like it very much and will tell every one having trouble with different based numbers about it.
Thanks a lot, Andreas!
Very comprehensive introduction! I liked how you demonstrated the evolution of Roman numerals, they never made too much sense for me until I read this post.
I went though the whole thing looking for a guitar or musical scale analogy.
It's a play on the word basses ;)