Skip to content

Reading 14: Binary and hexadecimal numbers

Unity can only be manifested by the Binary.
Unity itself and the idea of Unity are already two.

- Buddha

Overview

When you first learned to count, you learned decimal (base 10) numbers. Decimal numbers are convenient because they are pretty compact and because we have ten fingers. But computers almost never use decimal numbers internally. Instead, they use binary (base 2) numbers, and sometimes hexadecimal (base 16) numbers. In this reading we'll review these kinds of numbers.

Topics

  • Binary numbers
  • Hexadecimal numbers

Number representations

We normally compute using decimal numbers i.e. numbers composed of ten digits: 0, 1, 2, 3, 4, 5, 6, 7, 8, and 9. This is only one of many ways to represent numbers. Other ways include:

  • Roman numerals: MMXX (2020)
  • Unary numbers (sequence of 1s): 1111111111 (10)

There are actually an infinite number of possible number representations. Therefore, a single number has an infinite number of ways of being represented. But all number representations are not equally useful. In particular, two such representations are particularly convenient to use when representing numbers in a computer: binary (base 2) numbers and hexadecimal (base 16) numbers.

Writing numbers in different bases

Traditionally, when we write a number that isn't base 10, we specify the base of the number in a subscript coming after the number. If there is no subscript, we assume that it's decimal. Numbers with integer bases less than 10 (and greater than 0) use digits from 0 up to but not including the base itself. So:

  • 1234 is decimal: one thousand, two hundred and thirty-four.

  • 123410 is the same number, with an explicit subscript.

  • 100102 is a binary (base 2) number (18 in decimal).

  • 10202103 is a ternary (base 3) number; this number is 912 in decimal.

  • 4756608 is an octal (base 8) number; this number is 162736 in decimal.

Hexadecimal numbers are a little more complicated; we'll discuss them below.

We can't write subscripts in computer code, so there has to be another way to write literal numbers in different bases. We already know how to write decimal numbers; we'll see how to write binary and hexadecimal numbers below. These are the only number bases we will care about in this course.1

Binary numbers

A binary number is a number representation that uses only two digits, conventionally called 0 and 1. This representation is very convenient when building digital electronic circuits, because you only have to be able to distinguish between two physical quantities (e.g. "low voltage" and "high voltage"). Since all physical quantities have noise associated with them, it's easier to have robust circuits the fewer the number of distinguishable quantities there are, and two distinguishable quantities is the smallest practical number you can use. Modern computers are generally built out of electronic circuits, so they too use binary numbers.

We are more familiar with decimal (base 10) numbers, because that is the usual way to represent numbers. But it's important to realize that all actual computations on a computer happen using binary numbers. The computer has to be able to convert decimal numbers we enter (e.g. in source code) into the binary numbers that it uses. Then, when a (binary) numeric result is obtained, the computer has to convert it back to a decimal representation so we can see it the way we expect to see it.

Converting from binary to decimal

To gain familiarity with binary numbers, let's see how to convert a binary number to a decimal number. We will convert 110102 to decimal. What do we have to do?

First of all, let's recall what a decimal number actually means. The number 123410 actually means:

123410
= 1 × 103 + 2 × 102 + 3 × 101 + 4 × 100
= 1 × 1000 + 2 × 100 + 3 × 10 + 4 × 1
= 1000 + 200 + 30 + 4
= 1234

Note that 1000, 100, 10, and 1 are all powers of 10 (1 = 100).

In contrast, binary numbers use base 2, not base 10. Thus, the number 110102 means:

110102
= 1 × 25 + 1 × 24 + 0 × 22 + 1 × 21 + 0 × 20
= 1 × 16 + 1 × 8 + 0 × 4 + 1 × 2 + 0 × 1
= 16 + 8 + 0 + 2 + 0
= 26

Note that 16, 8, 4, 2, and 1 are all powers of 2 (1 = 20). So if we add up (in decimal) the powers of 2 corresponding to the digit locations that are 1 in the binary number (here, 16, 8, and 2), we get the decimal version of the number!

Be aware that a single "number" like 11010 can mean something very different in different number bases: 26 in base 2 and 11010 (eleven thousand and ten) in base 10. To be explicit about this, you should use the subscript.

Literal binary numbers in Python

Python allows you to write literal binary numbers using just 0s and 1s as long as you add the 0b prefix:

>>> 11010    # not a binary number
11010
>>> 0b11010  # a literal binary number because of the 0b prefix
26

This is rarely needed, but it's nice to have available.

Converting from decimal to binary

Converting numbers from binary to decimal is pretty straightforward. Converting from decimal to binary is a lot harder, and we're not going to do it here (though it may show up as an assignment problem).

Roundoff error

Python tries as hard as it can to disguise the fact that internal computations are done in binary, not in decimal, but occasionally you will see something weird that is a result of the conversions from decimal to binary and back. For instance:

>>> 4.1 / 100
0.040999999999999995

This seems like a pretty weird result. It should be the same as 41 / 1000, right?

>>> 41 / 1000
0.041

The 41 / 1000 result is correct; what's up with the other one? Well, you can see that it's very, very close to the "correct" answer, differing by only a small amount:

>>>  41 / 1000 - 4.1 / 100
6.938893903907228e-18

The number 6.938893903907228e-18 uses exponential notation; this is really 6.938893903907228 × 10-18. That's a very small error. We call this roundoff error. It happens because in order to compute 4.1 / 100, the computer first has to

  • convert 4.1 and 100 to their binary number equivalents

  • do the division using binary numbers

  • convert the result back into decimal numbers

Integers like 41 and 1000 have exact binary number representations. Floating-point numbers like 4.1 usually don't, so the computer has to approximate their values, which leads to a loss of numeric precision. Unless you are doing very exacting numerical calculations, this is unlikely to be a problem for you, and you shouldn't worry about it, but at least you know what's going on.2

Hexadecimal numbers

Binary numbers are great for computers, but they aren't great for humans to read, because they have a lot more digits than decimal numbers do. For instance:

  • 1000 (decimal) is 1111101000 (binary)
  • 1000000 (decimal) is 11110100001001000000 (binary)

Despite this, binary numbers are actually used to represent things that programmers can see. For instance, locations in memory are naturally expressed in terms of binary numbers. But nobody wants to read a large binary number with a zillion digits. There's a need for a more compact way to represent binary numbers.

To do this, we can use a little numerical trick: we can represent binary numbers as hexadecimal numbers, which means base 16 numbers. A hexadecimal number is a number that has the usual decimal digits 0 through 9, but which also has six new "digits": a, b, c, d, e, and f. Here, these "digits" do not represent the letters found in text, but have a specific meaning:

  • a means 10
  • b means 11
  • c means 12
  • d means 13
  • e means 14
  • f means 15

Note

Some people use uppercase letters instead of lowercase letters. It really doesn't matter.

A number in hexadecimal is represented as a sum of powers of 16. For example:

1a2e16
= 1 × 163 + a × 162 + 2 × 161 + e × 160
= 1 × 4096 + 10 × 256 + 2 × 16 + 14 × 1
= 4096 + 2560 + 32 + 14
= 6702

OK, fine. But what has this got to do with binary numbers?

The trick is that you can "chunk together" any adjacent group of four binary numbers to get a single hexadecimal number. The equivalence chart is as follows:

# binary -> hex
0000 -> 0
0001 -> 1
0010 -> 2
0011 -> 3
0100 -> 4
0101 -> 5
0110 -> 6
0111 -> 7
1000 -> 8
1001 -> 9
1010 -> a (10)
1011 -> b (11)
1100 -> c (12)
1101 -> d (13)
1110 -> e (14)
1111 -> f (15)

Here's an example of a conversion:

111101000010010000002           (1000000 in binary)
= 1111  0100  0010  0100  0000  (chunking into groups of 4 digits)
=    f     4     2     4     0  (convert to hexadecimal equivalents)
f424016

So 111101000010010000002 in hexadecimal is f424016, which is obviously much more compact. The only thing you have to be careful of is that you need to build the 4-binary-digit chunks from the right to the left, adding zeros at the end of the leftmost chunk to pad it out to exactly four digits if necessary. When you do this, you can convert any binary number to a hexadecimal number just by replacing the chunks with the corresponding digits.

Literal hexadecimal numbers

Python also allows you to write literal hexadecimal numbers if you use the 0x prefix:

>>> 0xf4240
1000000

An example from Python

Even though you may not need to use hexadecimal numbers yourself, you will still see them from time to time. For example:

>>> def double(x):
...     return x * 2
...
>>> double(42)
84
>>> double
<function double at 0x104495af0>

Look at the mysterious number 0x104495af0 in the last line. (If you type this in yourself, you will almost certainly get a different number.) This number indicates where in the computer's memory the double function is located. Locations in computer memory (usually called addresses) are normally identified using hexadecimal numbers. They could be identified using decimal (or even binary) numbers, but it's more natural to use hexadecimal numbers.

That's all that you will need to know about binary and hexadecimal numbers for this course.


  1. And, very probably, the only number bases you will encounter in your career. Octal numbers used to be used a lot, but they are very rarely used now. 

  2. There are computations where even small amounts of roundoff error can totally ruin the computation. Scientists who do a lot of numerical simulations have to learn special techniques to avoid these kinds of problems. These techniques are beyond the scope of this course.