Thimble Learning Platform
Back to full view

Number Bases: How binary, hex, and other number systems work

Introduction

Because of how computers work at a base electronic level, it's common to find code that makes use of numbers that are written differently than the decimal system that most people are used to. In this module, we'll first dissect the base-10 (decimal) system that we use every day, then apply what we've learned to change to other number systems, including binary and hexadecimal. Finally, we'll briefly talk about why almost all modern computers use a binary number system to perform calculations.

Objectives

  • Understand number bases: base-10, base-16, base-2
  • Convert numbers between bases
  • Have a general understanding of why we use different bases
  • Base10: The Decimal System

    Overview 

    The numbers that we use every day for things like prices, math, banking, how many pizzas to get for a party, etc are in a system called base10, or decimal. We call it that because each 'column', or place, is associated with a power of 10. The system also has a radix of 10, meaning that each column can hold 1 of 10 possible digits: 0, 1, 2, 3, 4, 5, 6, 7, 8, or 9.

    ex: 20310203_{10}

    Download file Copy to clipboard
    203=(2102)+(0101)+(3100)203 = (2 * 10^2) + (0 * 10^1) + (3 * 10^0)
    203=200+0+3203 = 200 + 0 + 3
    

    The digit in each place is multiplied by a power of 10, and then when the products are added together, the result is the number written in base10. The 1's column is multiplied by 10010^0 , which is just 1 , the 10's column is multiplied by 101=1010^1 = 10 , the 100's column is multiplied by 102=10010^2 = 100 , and so on. It's a different way of looking at the numbers we use every day, but it hopefully gives you some insight into how and why they are constructed the way that they are.

    Note: The subscript 10 is used to denote the base of the number. We don't include this in every day math because it's assumed that unless specified, numbers are already written in base10.

    Numbers smaller than 1 

    It's a pretty straight-forward process for getting bigger and bigger numbers- just increase the power of 10 of the multiplier. But what about numbers smaller than 1?

    Let's look at 0.31100.31_{10} as an example. Thinking in the context of number bases, we can see that the decimal point actually specifies where our 10010^0 column is. When moving to the right of the decimal point, the powers of 10 simply continue to decrease:

    ex. 0.31100.31_{10}

    Download file Copy to clipboard
    0.31=(0100)+(3101)+(1102)0.31 = (0 * 10^0) + (3 * 10^{-1}) + (1 * 10^{-2})
    0.31=0+0.3+.010.31 = 0 + 0.3 + .01
    

    Recall that:

    Download file Copy to clipboard
    101=1101=110=0.110^{-1} = \frac{1}{10^1} = \frac{1}{10} = 0.1
    102=1102=1100=0.0110^{-2} = \frac{1}{10^2} = \frac{1}{100} = 0.01
    103=1103=11000=0.00110^{-3} = \frac{1}{10^3} = \frac{1}{1000} = 0.001
    

    If you've never experienced numbers in different bases before, hopefully this is some interesting insight into the number system we use every day. Now that we've figured out how our decimal system works as written, it should be an easy task to apply this to other number bases.

    General Base Conversions

    Converting to base10 

    In the previous block, we broke down our every day base10 system into columns, multiplied by a power of 10, then added together. With this method, we can build systems using any number for a base by just changing the powers. For example, let's examine a ternary (base3) numbering system. In a base3 system, each column represents a power of 3 (rather than 10, as in the decimal system). Also, the system has a radix of 3, meaning each column can take on only 3 different values: 0, 1, or 2.

    ex. Write the base3 number 2123212_{3} in the decimal (base10) system:

    Download file Copy to clipboard
    (the right-hand-side of the equations are all in base10)
    2123=(232)+(131)+(230)212_{3} = (2 * 3^2) + (1 * 3^1) + (2 * 3^0)
    2123=(29)+(13)+(21)212_{3} = (2 * 9) + (1 * 3) + (2 * 1)
    2123=1810+310+210212_{3} = 18_{10} + 3_{10} + 2_{10}
    2123=2310212_{3} = 23_{10}
    

    So the number 2123212_{3} , a number written using 3 as a base, is the equivalent of 231023_{10} in the base10 system.

    Converting a non-decimal system to base10 is straight-forward:

    1. In the base10 system (where we're used to doing our math), multiply each digit from the number in the alternate base (base3 in the above example) by the power of the base that's appropriate for that column.

    2. Add the products together (still in base10). The result is the number that was originally written in an alternate base, now written in base10.

    Converting from base10 

    The exact same process for converting to base10 can be used to convert from base10 to another base. However, all of the math on the right-hand-side ( multiplication and addition) would need to be done in the alternate base system for it to work. So while it's possible... our brains are used to doing math in base10, so usually use subtraction instead. Here's an abstract step-by-step (we'll take a look at an example right after so it makes more sense):

    1. Find the highest multiple * power of the base that will fit into the base10 number.

    2. Write the alternate base number in the correct column, leaving blanks to the right of it (to fill in later steps).

    3. Subtract the amount equivalent to the alternate base number from your current base10 number. This is now your remainder.

    4. Jump back to step 1, using this remainder instead of your original number.

    5. Repeat steps 1-4 until your remainder is 0.

    ex. Write the decimal number 23510235_{10} in a base4 system.

    Find the highest mulitple * power that will fit in 23510235_{10} . In base10, 44=2564^4 = 256 , which is too high. So we'll start 43=644^3 = 64 for our power, then 3 for our multiple- so we get 343=1923 * 4^3 = 192 . Remember, because it's a base4 system, the radix is 4, meaning that each column can only take on 4 possible values: 0, 1, 2, 3.

    We'll place a 3 in the 434^3 column, which is the 4th one to the left. The remaining places we'll leave blank

    Download file Copy to clipboard
    23510=3___4235_{10} = 3\_\_\__{4}
    

    Now we can subtract 192 from our original 235 , since we're accounting for that 192 with the 3 in the 4th column of our new base4 number.

    Download file Copy to clipboard
    2351019210=4310235_{10} - 192_{10} = 43_{10}
    

    Using our remainder of 431043_{10} , jump back to step 1 and continue the process. The next column is 43=164^3 = 16 , and the highest multiplier of 161016_{10} that will fit into 431043_{10} is 2: 162=3216 * 2 = 32 . So we'll put a 2 into the 424^2 column.

    Download file Copy to clipboard
    23510=32__4235_{10} = 32\_\__{4}
    

    Subtract our newest multiplier from our working remainder:

    Download file Copy to clipboard
    43103210=111043_{10} - 32_{10} = 11_{10}
    

    So our new remainder is 11. Repeating the process again, our next column's power of 4 is 1 . The highest multiplier is 2 ( 24=82 * 4 = 8 ):

    Download file Copy to clipboard
    23510=322_4235_{10} = 322\__{4}
    

    Calculate our new remainder:

    Download file Copy to clipboard
    1110810=31011_{10} - 8_{10} = 3_{10}
    

    With the new remainder of 3, and the final column being essentially a 1's column, we can fill in the final column with just 3 :

    Download file Copy to clipboard
    23510=32234235_{10} = 3223_{4}
    

    So 23510235_{10} written in base4 is 322343223_{4}

    It might seem like a lot of work, but luckily, most of a programmer's work in other number systems involves only a handful of other bases. The more you work in these other base systems, the easier it is to convert back and forth. The two most common systems that you'll see while programming are binary, a base2 system, and hexadecimal, a base16 system. Other common systems are base8 (octal) and base64.

    Binary and Hexadecimal

    Binary 

    Binary is another name for a base2 number system, meaning each column represents a power of 2, and the radix (the number of unique digits in the system) is 2. So each column/place in a binary number can have one of two values: 0 or 1 . In programming, binary numbers (binary literals) are often represented by putting a b out front: b10011101 is the equivalent of 10011101210011101_{2} .

    Let's start by converting a binary number to decimal, using the same technique used earlier:

    ex. Write the binary number 110121101_{2} in decimal (base10):

    Recall that the right hand side (where we do our math) is in base 10

    Download file Copy to clipboard
    11012=(123)+(122)+(021)+(120)1101_{2} = (1 * 2^3) + (1 * 2^2) + (0 * 2^1) + (1 * 2^0)
    11012=8+4+0+11101_{2} = 8 + 4 + 0 + 1
    11012=13101101_{2} = 13_{10}
    

    We found that b1101 is equal to 13 in decimal. Let's try going the other way:

    ex. Write the decimal number 11610116_{10} in binary:

    The largest power of 2 that fits in to 116 is 6 ( 26=642^6 = 64 ), so we'll start in the 7th column (the 1st column represents 20=12^0 = 1 ):

    Download file Copy to clipboard
    11610=1______2116_{10} = 1\_\_\_\_\_\__{2}
    116106410=5210116_{10} - 64_{10} = 52_{10}
    

    The next power of 2 that fits in to our remainder of 52 is 25=322^5 = 32 :

    Download file Copy to clipboard
    11610=11_____2116_{10} = 11\_\_\_\_\__{2}
    52103210=201052_{10} - 32_{10} = 20_{10}
    

    The next power of 2 that fits in to our remainder of 20 is 24=162^4 = 16 :

    Download file Copy to clipboard
    11610=111____2116_{10} = 111\_\_\_\__{2}
    20101610=41020_{10} - 16_{10} = 4_{10}
    

    Now, we've reached a point where the next power of 2, 23=82^3 = 8 is too big to fit into our remainder. We fill it's place with a 0 and move on to the next power:

    Download file Copy to clipboard
    11610=1110___2116_{10} = 1110\_\_\__{2}
    410010=4104_{10} - 0_{10} = 4_{10}
    

    The next power fits right in ( 22=42^2 = 4 ):

    Download file Copy to clipboard
    11610=11101__2116_{10} = 11101\_\__{2}
    410410=0104_{10} - 4_{10} = 0_{10}
    

    It also leaves us with 0 for a remainder. Since the only multiple of 0 is 0, we can be pretty confident that there are no remaining powers of two that will fit into our answer. Thus, we can fill the rest of the blanks with 0's :

    Download file Copy to clipboard
    11610=11101002116_{10} = 1110100_{2}
    

    Hexadecimal 

    Hexadecimal, or just 'hex', is the most common number system other than decimal that you will see in programming. Hexadecimal numbers are written in base16; having a radix of 16 allows for larger numbers to be written with less symbols, but also means that you need at least 16 different symbols to write these numbers. Using arabic numerals, we only have 10 digits, so we fill the remaining symbols in with the letters A-F.

    That means that each column in a hexadecimal number can take on a value between 0 and F, with the following equivalence:

    base16 base10
    0-9 0-9
    A 10
    B 11
    C 12
    D 13
    E 14
    F 15

    In programming, or computing in general, hex numbers can be written in a number of ways. Most commonly, they're hexadecimal literals are written as 0x3A , with the leading 0x signifying that the following is a base16 number. The leading 0 may also be replaced with a slash, or other character, but generally there is an x that lets you know the number is in hex. Because hexadecimal is so common in computing, oftentimes there won't be any sign at all that a number is written in hex- but you can usually tell from the context or the mix of numbers and letters A-F.

    Let's do a quick conversion, hexadecimal to decimal as an example:

    ex. Write 0xF1 in decimal:

    Download file Copy to clipboard
    (right-hand-side in base10)
    F116=(15161)+(1160)F1_{16} = (15 * 16^1) + (1 * 16^0)
    F116=240+1F1_{16} = 240 + 1
    F116=24110F1_{16} = 241_{10}
    

    Notice that we used 1 character less when writing the number in hexadecimal versus decimal. If we had a lot of numbers to store (millions or billions) as text in something like a document, it would be much more space efficient to store them as hex rather than decimal.

    Binary to Hex and Back 

    The relationship between base2 (binary) and base16 (hex) makes it extremely simple to convert back and forth between the two. Each column in hexadecimal can be comprised of 4 columns of binary and vice-versa. Converting between the two is as easy knowing the numbers 0-15 in both binary and hex.

    decimal hex binary
    0 0 0000
    1 1 0001
    2 2 0010
    3 3 0011
    4 4 0100
    5 5 0101
    6 6 0110
    7 7 0111
    8 8 1000
    9 9 1001
    10 A 1010
    11 B 1011
    12 C 1100
    13 D 1101
    14 E 1110
    15 F 1111

    ex. Convert 0x2F9A to binary:

    We'll just look at each column one at a time, and use the chart to convert:

    Download file Copy to clipboard
    216=001022_{16} = 0010_{2}
    F16=11112F_{16} = 1111_{2}
    916=100129_{16} = 1001_{2}
    A16=10102A_{16} = 1010_{2}
    

    After we have the binary number for each hex digit, write them in order to obtain the complete binary equivalent. Note that it's standard to put a separator (in this case, a space) every 4 digits in binary to help with readability:

    Download file Copy to clipboard
    (2F9A)16=(0010 1111 1001 1010)2(2F9A)_{16} = (0010\ 1111\ 1001\ 1010)_{2}
    

    To convert a binary number to hex, the same strategy applies, but in reverse. The binary number is broken up into groups of 4, and then the equivalent hex value is substituted:

    ex. Convert b1101011 to hexadecimal:

    First note that there are only 7 digits in the binary number. To be able to separate it into 2 groups of 4 digits, we can simply add a 0 out front, then do the conversion:

    Download file Copy to clipboard
    01102=6160110_{2} = 6_{16}
    10112=B161011_{2} = B_{16}
    (0110 1011)2=(6B)16(0110\ 1011)_{2} = (6B)_{16}
    

    If you've been around computers enough, you may have noticed that a lot of numbers occur in powers of 2: you might have a 256 gigabyte SSD in your computer, or 2048 bytes of SRAM on an Arduino's microcontroller. Binary, and thus powers of 2, is the base number system with which all modern computers operate. In the final block, we'll discuss the very basics of why that is.

    Relation to Computers

    Why Binary? 

    It's common knowledge that nearly all modern computers operate with binary, but the reasons for that aren't as clear without looking at the fundamental electrical components of a computer. Computers are machines of logic, where everything is always in one of two states: True or False . It's a pretty simple leap to say that we can represent True and False with the binary number system's 1 and 0 - they're both binary systems, just using different symbols for each 'column'.

    Transistors 

    TransistorsTransistors

    The base electrical component used to build the logic circuits of a computer is the transistor, which is essentially an on/off switch that can be controlled electronically (similar to a relay). On and Off is of course another binary system, so the physical state of an electrical circuit- that is, a switch being On is conducting electricity and a switch being Off is not conducting- can be used to represent things like True and False or 0 and 1 .

    When transistors connected together and arranged in particular ways, we can form logic gates, leading to basic logic blocks that can store information, do math, and eventually guide a ship to the moon.

    Bits, nibbles, and bytes 

    Even though computers operate with individual 1 's and 0 's (bits), it's not efficient for human designers or operators to work at this level. Instead, the smallest useable piece of information we can usually read or write to on a computer is larger- typically a block of 8 bits (an octet) at the smallest. With modern computers, a block of 8 bits is referred to as a byte.

    We saw in the previous block that it was really convenient to store information using number systems with a higher base than 10. We also observed that hexadecimal fits great with binary- as long as it's arranged in blocks of 4 digits. In computing, a block of 4 binary digits is commonly referred to as a nibble (...a small 'byte'). With each hexadecimal digit equal to a nibble (a series of 4 bits), we can write a single byte (the base unit of information on a computer, made up of 2 nibbles or 8 bits), using just two hexadecimal digits.

    After you've read that last sentence 3 or 4 times and the meaning has become clear, hopefully you'll come to see why it's often convenient for programmers to write out values in hexadecimal in their code. It's especially useful when not referencing an actual number, but instead a series of bits. For example, when controlling a string of LEDs with an Arduino, you might have the following sequence:

    Download file Copy to clipboard
    1
    On-Off-On-On-Off
    

    On/off is just 1's and 0's to a computer, so the same thing written in binary would be:

    Download file Copy to clipboard
    1
    10110
    

    Which is just a number. In base10 (decimal), that number is 22. The phrase "set the LED string to 22" has no physical meaning though, so you'll often find 'numbers' like this written out using either hex or binary literals:

    Download file Copy to clipboard
    1
    2
    3
    4
    5
    led_string = b00010110
    
            or
    
    led_string = 0x16
    

    Note: A lot of languages don't have/allow binary literals, so it's much more common to see and use hexadecimal. As a rule of thumb, always use hex values in your code, and if you feel it's necessary, you can write the binary values in nearby comments.

    Terms Used

    Definitions 

    base, radix: In mathematical numeral systems, the radix or base is the number of unique digits, including zero, used to represent numbers in a positional numeral system. For example, for the decimal system (the most common system in use today) the radix is ten, because it uses the ten digits from 0 through 9 1.

    binary: A number system with radix 2, commonly used digital logic circuitry.

    hexadecimal: A number system with radix 16, widely used by computer programmers as a human-friendly representation of binary values.

    transistor: An electrical component used as an electrically-controlled switch in digital logic circuits 2.

    bit: A logic 1 or 0 (True or False) in computer circuitry.

    byte: A combination of 8 bits.

    nibble: Half of a byte, or 4 bits.

    Further Reading

    Links 

    All About Circuits - Numeration Systems

    Arduino - Integer Constants

    Sparkfun Tutorials - Transistors

    Sparkfun Tutorials - Digital Logic

    Also, if you're interested in playing around with digital logic and enjoying some Minecraft at the same time, Redstone circuits are a great way to learn and practice the basics. If you've ever seen videos of people building counters, adders, and mini-computers in Minecraft, binary math and basic digital logic are where they started.