One Plus One

Bits

Because computers are made of switches, data is made of bits. A bit is an abstraction of a physical phenomenon that can either be “on” or “off.” The mapping between the physical phenomenon and the 0 or 1 digit is arbitrary; this is just something that humans have to make up. For example

  • In a hydraulic computer, maybe 0 is “no water” and 1 is “water is flowing.”
  • In Turing Tumble, perhaps 0 is “marble goes left” and 1 is “marble goes right.”
  • In an electronic computer, let’s use 0 to to mean “low voltage” and 1 to mean “high voltage.”

Binary Numbers

Armed with switches and a logical mapping, computers have a way to represent numbers! Just really small numbers: a bit suffices to represent all the integers in interval [0, 1]. It would be nice to be able to represent numbers bigger than 1.

We do that by combining multiple bits together and counting in binary, a.k.a. “base 2.”

In elementary school math class, you probably learned about “place values.” The rightmost digit in a decimal number is for the ones, the next one is for tens, and the next one is for hundreds. In other words, if you want to know what the string of decimal digits “631” means, you can multiply each digit by its place value and add the results together:

\[ 631_{10} = 1 \times 10^0 + 3 \times 10^1 + 6 \times 10^2 \]

We’ll sometimes use subscripts, like \( n_{b} \), to be explicit when we are writing a number in base \( b \).

That’s the decimal, a.k.a. “base 10,” system for numerical notation. Base 2 works the same way, except all the place values are powers of 2 instead of powers of 10. So if you want to know what the string of binary digits “101” represents, we can do the same multiply-and-add dance:

\[ 101_2 = 1 \times 2^0 + 0 \times 2^1 + 1 \times 2^2 \]

That’s five, so we might write \( 101_2 = 5_{10} \).

Some Important Bases

We won’t be dealing with too many different bases in this class. In computer systems, only three bases are really important:

  • Binary (base 2).
  • Octal (base 8).
  • Hexadecimal (base 16), affectionately known as hex for short.

Octal works exactly as you might expect, i.e., we use the digits 0 through 7. For hexadecimal, we run out of normal human digits at 9 and need to invent 6 more digits. The universal convention is to use letters: so A has value 10 (in decimal), B has value 11, and F has value 15.

Converting Between Bases

Here are two strategies for converting numbers between different bases. In both algorithms, it can be helpful to write out the place values for the base you’re converting to. We’ll convert the decimal number 637 to octal as an example. In octal, the first few place values are 1, 8, 64, and 512.

Left to Right

First, compute the first digit (the most significant digit) finding the biggest place value you can that is less than that number. Then, find the largest number you can multiply by that place value. That’s your converted digit. Take that product (the place value times that largest number) and subtract it from your value. Now you have a residual value; start from the beginning of these instructions and repeat to get the rest of the digits.

Let’s try it by converting 637 to octal.

  • The biggest place value under 636 is 512. \( 512 \times 2 \) doesn’t stay “under the limit,” so we have to settle for \( 512 \times 1 \). That means the first digit of the converted number is 1. The residual value is \( 637 - 512 \times 1 = 125 \).
  • The value that “fits under” 125 is \( 64 \times 1 \). So the second digit is also 1. The residual value is \( 125 - 64 \times 1 = 61 \).
  • We’re now at the second-to-least-significant digit, with place value 8. The largest multiple that “fits under” 61 is \( 8 \times 7 \), so the next digit is 7 and the residual value is \( 61 - 8 \times 7 = 5 \).
  • This is the ones place, so the final digit is 5.

So the converted value is \( 1175_8 \).

Right to Left

First, compute the least significant digit by dividing the number by the base, \(b\). Get both the quotient and remainder. The remainder is the number of ones you have, so that’s your least significant digit. The quotient is the number of \(b\)s you have, so that’s the residual value that we will continue with.

Next, repeat with that residual value. Remember, you can think of that as the number of \(b\)s that remain. So when we divide by \(b\), the remainder is the number of \(b\)s and the quotient is the number of \(b^2\)s. So the remainder is the second-to-least-significant digit, and we can continue around the loop with the quotient. Stop the loop when the residual value becomes zero.

Let’s try it again with 637.

  • \( 637 \div 8 = 79 \) with remainder 5. So the least significant digit is 5.
  • \( 79 \div 8 = 9 \) with remainder 7. So the next-rightmost digit is 7.
  • \( 9 \div 8 = 1 \) with remainder 1. The next digit is 1.
  • \( 1 \div 8 = 0 \) with remainder 1. So the final, most significant digit is 1.

Fortunately, this method gave the same answer: \( 1175_8 \).

Programming Language Notation

When writing, we often use the notation \( 1175_8 \) to be explicit that we’re writing a number in base 8 (octal). Subscripts are hard to type in programming languages, so they use a different convention.

In many popular programming languages (at least Java, Python, and the language we will use in 3410: C), you can write:

  • 0b10110 to use binary notation.
  • 0x123abc to use hexadecimal notation.

Octal literals are a little less standardized, but in Python, you can use 0o123 (with a little letter “o”).

Addition

To add binary numbers, you can use the elementary-school algorithm for “long addition,” with carrying the one and all that. Just remember that, in binary, 1+1 = 10 and 1+1+1 (i.e., with a carried one) is 11.