github linkedin email rss
A Quick Intro To Binary, Octal, Decimal, Hexadecimal - Oh My!
2019-02-15
9 minutes read

Our everyday computers are a collection of electronic circuits, only capable in working “on”, or “off” - 1, and 0 (incidentally, there are alternate systems that use more than 1 and 0).

But humans don’t work in 1s and 0s. When we work with numbers, we say things like ‘20 cats’ (that’s a lot of cats!) or ‘5 pieces of pizza’ (I swear I didn’t eat all of it).

How is a computer, which is only capable of working in 1s and 0s, able to represent numbers higher than 1?

Caveats

My usual caveats apply: this is a beginner introduction, and doesn’t dive through some of the more conceptual parts of numeric bases and how they work.

Working in Tens

Let’s look at a regular number: 216. It comes after 215, and before 217. It has three digits: 2, 1, and 6 in that order.

Straightforward, right?

When you were learning ‘big’ numbers back in your very first year of school, you may have worked with the “tens” column - maybe even the “hundreds” column, when you got really good. Our number 216 is really a sum of three numbers:

       2 × 100 = 200
     + 1 ×  10 =  10
     + 6 ×   1 =   6
     ---------------
     =           216

Or, if we draw it another way, with our “hundreds”, “tens”, and “ones” columns:

     100s |  10s |  1s
    -------------------
       2  |   1  |   6  

Why is it “tens” and “hundreds”? Because each digit (each column) has ten possible choices: 0,1,2,3,4,5,6,7,8,9. Every time one column reaches the maximum of 9, it rolls around to 0 and the next column increases by 1: e.g.

    Increase by one each time

     100s |  10s |  1s
    -------------------
       2  |   1  |   6  
       2  |   1  |   7
       2  |   1  |   8
       2  |   1  |   9
       2  |   2  |   0     <--- The 10s column increased!

This may seem obvious; but nevertheless, keep this in mind - it’s going to become useful.

Getting Back to Base-ics

Let’s redraw our columns like this

     10² | 10¹ | 10⁰
    -----------------
      2  |  1  |  6

This is exactly the same representation as before - the rightmost column is multiplied by 1… or 10⁰. The next is multiplied by 10, or 10¹; again with our 100s, or 10² - and so on.

Because each of our digits has just ten possible positions, we’re working in “Base 10”. Each column (each digit) is just the base, 10, multiplied by the column number (counting the columns from 0).

We also call this system decimal (coming from the Latin ‘decem’ meaning ‘ten’). We can denote a number written in Base 10 by writing the base as a subscript, e.g. (216)10 or, more commonly (216)dec. However, since Decimal is the most commonly used system worldwide, we usually don’t bother.

So it goes when we want to work in another base: e.g. Base 8, or Octal (from the Ancient Greek ‘okto’ meaning ‘eight’)

As before, Base 8 means each digit has just 8 positions: 0,1,2,3,4,5,6,7. After we reach where an ‘8’ would be in decimal, the next column rolls over. E.g.

    # Increase by one each time

      8² |  8¹ |  8⁰
    -------------------
      1  |  0  |   4
      1  |  0  |   5
      1  |  0  |   6
      1  |  0  |   7
      1  |  1  |   0    <--- The 8s column increased!

Let’s take a look at our number (216)dec but written in octal (with each column expanded):

      8² |  8¹ |  8⁰
     64  |  8  |  1
    -----------------
      3  |  3  |  0  

Instead of “ones”, “tens”, “hundreds” columns, we now have “ones”, “eights”, “sixty-fours” columns; just as before, each column is a multiple of our base, 8, multiplied by the column number.

To see how (330)₈ = (216)₁₀, let’s convert (330) 8 into Base 10 - we can do it by multiplying each of the columns like we did with (216) dec :

    # Converting (330)₈ to Decimal

       3 × 64 = 192
     + 3 ×  8 =  24
     + 0 ×  1 =   0
     --------------
     =          216

And just like that, we’re back at 216. To go back the other way, we can use division:

    # Converting (216)₁₀ to Octal

    216 ÷ 8² = 216 ÷ 64
             =        3 remainder 24
    -------------------
     24 ÷ 8¹ =  24 ÷  8
             =        3 remainder  0
    -------------------
      0 ÷ 8⁰ =   0 ÷  1
             =        0 remainder  0

    # Our digits are [3,3,0] or (330)₈

For each column, we divided it into our decimal number, taking the whole result; then we go to the next column, and divide it into the remainder. We’ll never be left with a remainder at the end since our final division is by (base)⁰, which is always 1… and 1 goes into everything!

How did we know to start with 8² (64), and not 8³ (512) or a higher number? You can start that way, but 512 is too big to divide into 216, and will just leave us with the original number as our remainder.

Let’s Get Hexy

Now we’ve looked at Base 8, let’s take a look at another base that’s common in computing: Base 16, or Hexadecimal (from latin ‘hexa’ meaning ‘six’)

We know each digit has a maximum of 16 positions: 0,1,2,3,4,5,6,7,8,9… wait, what’s next? We don’t have symbols to denote a tenth, eleventh, or higher position! Of course, we could invent some symbols, but commonly we just use the capital letters A through F.

That means our 16 positions for each digit are: 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F - or, in Decimal:

    # How does each digit compare between Hexadecimal (Hex) and Decimal (Dec)?

    Hex | Dec
    ---------
      0 |   0
      1 |   1
      2 |   2
      3 |   3
      4 |   4
      5 |   5
      6 |   6
      7 |   7
      8 |   8
      9 |   9
      A |  10
      B |  11
      C |  12
      D |  13
      E |  14
      F |  15

Let’s take a look at our number (216)dec but written in hexadecimal (and expanded in each column)

     16² | 16¹ | 16⁰
    256  | 16  |  1
    -----------------
         |  D  |  8  

Instead of “ones”, “tens”, “hundreds” columns, we now have “ones”, “sixteens”, “two hundred fifty-sixes” for each digit.

The third column is also empty - Whereas 216 is three digits long in base 10, it only needs two digits to be represented in Base 16.

Let’s confirm (D8)₁₆ = (216)₁₀

    # Converting (D8)₁₆ to Decimal

      D × 16 = 13 × 16 = 208
    + 8 ×  1 =  8 ×  1 =   8
    ------------------------
    =                    216

Where did the 13 come from? As you saw in the column earlier, the symbol D in Hexadecimal really represents 13 in Decimal - to return our number into Decimal format, we need to bring those symbols into Base 10 format as well.

And just like that, we’re back at 216. To go back the other way, we can use division:

    # Converting (216)₁₀ to Hexadecimal

    216 ÷ 16² = 216 ÷ 256
              =         0 remainder 216
    ---------------------
    216 ÷ 16¹ = 216 ÷  16
              =        13 remainder   8
    ---------------------
      0 ÷ 16⁰ =   8 ÷   1
              =         8 remainder   0

    # Our digits are [13,8] or (D8)₁₆

Here we can see, as mentioned in the octal section, what happens if you start with a column that’s too big (too far to the left) - you end up with a zero result, and the original number as the whole remainder.

It’s All Ones And Zeroes In The End

Computers can only work in 1s and 0s (on, or off); what that really means is that it works in a number system where there’s two positions: 0,1 - known as Base 2 or Binary (from the latin ‘bini’ meaning ‘two together’)

Now that we’ve worked through Decimal, Octal, and Hexadecimal, we can see how (216)dec looks in binary

      2⁷ |  2⁶ |  2⁵ |  2⁴ |  2³ |  2² |  2¹ |  2⁰
    128  | 64  | 32  | 16  |  8  |  4  |  2  |  1
    -----------------------------------------------
      1  |  1  |  0  |  1  |  1  |  0  |  0  |  0 

Instead of “ones”, “tens”, “hundreds” columns, we now have “ones”, “twos”, “fours”,… columns - a multiple of our base in each column.

The method to convert (216)dec to (11011000)bin and vice versa is the same as with hexadecimal and octal

    # Converting (11011000)₂ to Decimal

           1 × 128 = 128
         + 1 ×  64 =  64
         [...]
         + 0 ×   2 =   0
         + 0 ×   1 =   0
         ---------------
         =           216

    # And vice versa, converting (216)₁₀ to Binary:

        216 ÷ 2⁷ = 216 ÷ 128
                 =         1 remainder 88
       ---------------------
         88 ÷ 2⁶ =  88 ÷  64
                 =         1 remainder  24
       ---------------------
       ... and so on ...
       ---------------------
         0 ÷  2¹  =   0 ÷  2
                  =        0 remainder  0
       ---------------------
         0 ÷  2⁰  =   0 ÷  1
                  =        0 remainder  0

    # Our digits are [1,1,0,1,1,0,0,0] or (11011000)₂

A Few Final Notes

There’s a few more things you may find of interest, as you work in computing

Non-Decimal Numbers In Programming

When you write a number in most programming languages, it’s assumed to be in decimal; but most languages also have a way to denote binary, octal, and hexadecimal numbers.

Here’s a couple of short examples:

    # Python
    dec_number = 216 # A standard decimal number
    bin_number = 0b11011000 # 216 in binary
    oct_number = 0o330 # 216 in octal
    hex_number = 0xD8 # 216 in hex
    // C/C++
    int dec_number = 216;
    int bin_number = 0b11011000; // WAIT: Only in C++14 or later. 
                                 // Not standard in C
    int oct_number = 0330;  // The preceding 0 is significant, 
                            // and tells the compiler it's an octal
    int hex_number = 0xD8; 

More Advanced Numbers

We haven’t touched on how to handle negative numbers… or anything more advanced like numbers with a decimal point (e.g. 2.154). They can be done, but these are both for future articles.

Conclusion

That was a whirlwind rundown on different common numeric bases you’ll find throughout computing - what they are, and how they work - though the principles for converting between bases are the same for any numeric base you may come across.

Hopefully now you have a better understanding of how a computer is able to work with any number, even though it can only work in 1 and 0.


Back to posts