Number Bases
From Compsci.ca Wiki
Line 135: | Line 135: | ||
|- | |- | ||
| 16 || 5 || 1 | | 16 || 5 || 1 | ||
+ | |- | ||
+ | | 4 || 2 || 0 | ||
|} | |} | ||
+ | |||
+ | Let's consider a simple conversion: represent the decimal number 1 in binary. | ||
+ | |||
+ | We'll get the result of 1 mod b where b = 2. Since we want position zero, this becomes 1 mod 2, which we know must be 1. We'll then divide 1 by 2 and get zero. Since we have zero remaining, we don't need any further digits to represent 1 in binary. | ||
+ | |||
+ | Let's consider a slightly more complex example. | ||
+ | |||
+ | {| style="text-align: center" border="1" cellpadding="5" cellspacing="0" | ||
+ | |- | ||
+ | ! colspan="2" | Representing decimal number 234 in binary | ||
+ | |- | ||
+ | ! Decimal || Modulo 2 | ||
+ | |- | ||
+ | | 234 || 0 | ||
+ | |- | ||
+ | | 117 || 1 | ||
+ | |- | ||
+ | | 58 || 0 | ||
+ | |- | ||
+ | | 29 || 1 | ||
+ | |- | ||
+ | | 14 || 0 | ||
+ | |- | ||
+ | | 7 || 1 | ||
+ | |- | ||
+ | | 3 || 1 | ||
+ | |- | ||
+ | | 1 || 1 | ||
+ | |- | ||
+ | | 0 || 0 | ||
+ | |- | ||
+ | ! colspan="2" | Final result = 11101010 | ||
+ | |} | ||
+ | |||
+ | === Programming === | ||
+ | |||
+ | When we consider the repetitive nature of this process, it should be quite easy to map it to code in a programming language using either procedural iteration or recursion. | ||
+ | |||
+ | ==== Python 3.0 Example ==== | ||
+ | |||
+ | <pre>>>> def to_binary(n): | ||
+ | ... digits = [] | ||
+ | ... while n > 0: | ||
+ | ... digits.insert(0, n % 2) | ||
+ | ... n //= b | ||
+ | ... return digits | ||
+ | ... | ||
+ | >>> to_binary(4) | ||
+ | [1, 0, 0] | ||
+ | >>> to_binary(234) | ||
+ | [1, 1, 1, 0, 1, 0, 1, 0]</pre> | ||
+ | |||
+ | ==== Making it more generic ==== | ||
+ | |||
+ | This algorithm doesn't need to specify the base at all. | ||
+ | |||
+ | ==== More Generic Python 3.0 Example ==== | ||
+ | |||
+ | <pre>>>> def to_base(n, b): | ||
+ | ... digits = [] | ||
+ | ... while n > 0: | ||
+ | ... digits.insert(0, n % b) | ||
+ | ... n //= b | ||
+ | ... return digits | ||
+ | ... | ||
+ | >>> to_base(4, 2) | ||
+ | [1, 0, 0] | ||
+ | >>> to_base(234, 2) | ||
+ | [1, 1, 1, 0, 1, 0, 1, 0]</pre> | ||
+ | |||
+ | ==== (Tail) Recursive O'Caml Example ==== | ||
+ | |||
+ | <pre> Objective Caml version 3.11.0 | ||
+ | |||
+ | # let to_base n b = | ||
+ | let rec helper n acc = | ||
+ | if n = 0 then acc | ||
+ | else helper (n / b) ([n mod b] @ acc) | ||
+ | in | ||
+ | helper n [];; | ||
+ | val to_base : int -> int -> int list = <fun> | ||
+ | # to_base 4 2;; | ||
+ | - : int list = [1; 0; 0] | ||
+ | # to_base 234 2;; | ||
+ | - : int list = [1; 1; 1; 0; 1; 0; 1; 0] | ||
+ | #</pre> |
Revision as of 03:01, 1 April 2009
Contents |
Number Bases
In math and computer science, numbers are often taken for granted, but understanding them is absolutely essential. The first thing to understand about numbers is how they're written, and for that we have to understand number bases.
Decimal
The base we're all raised on these days is base 10. We all know that in decimal representation "1" is one, "2" is two" and "456" is four hundred and fifty-six, but how does this actually work? How do we know that that "4" being where it indicate four hundred and how does the 5 being where it is indicate fifty?
In the case opf 456, we can see this as follows.
456 broken down | ||
---|---|---|
4 x 100 | 5 x 10 | 6 x 1 |
Of course, if you know anything about exponents, you should notice that there's a very simple pattern here.
456 broken down further | ||
---|---|---|
4 x 100 | 5 x 10 | 6 x 1 |
4 x 102 | 5 x 101 | 6 x 100 |
We can factor this out further
Factoring out the base | ||
---|---|---|
4 x 100 | 5 x 10 | 6 x 1 |
4 x 102 | 5 x 101 | 6 x 100 |
4 x b2 | 5 x b1 | 6 x b0 |
Now we should be able to extrapolate this pattern out to deal with decimal numbers of any length.
Binary
Binary is a very formal way of saying "base 2." In base 2 the only digits at our disposal for representations are 0 and 1. Let's break down a binary number.
Breakdown of 11001101 | |||||||
---|---|---|---|---|---|---|---|
1 x 128 | 1 x 64 | 0 x 32 | 0 x 16 | 1 x 8 | 1 x 4 | 0 x 2 | 1 x 1 |
1 x 27 | 1 x 26 | 0 x 25 | 0 x 24 | 1 x 23 | 1 x 22 | 0 x 21 | 1 x 20 |
1 x b7 | 1 x b6 | 0 x b5 | 0 x b4 | 1 x b3 | 1 x b2 | 0 x b1 | 1 x b0 |
Equivalent Decimal Representation | |||||||
128 | 64 | 0 | 0 | 8 | 4 | 0 | 1 |
When we add the decimal equivalents together using the addition we all know quite well, we get 205.
Hexidecimal
Also known as "base 16." Zero to nine are represented as one familiar with decimal would expect, while ten through fifteen are represented by A-F.
Breakdown of A9 | |
---|---|
A x 16 | 9 x 1 |
A x 161 | 9 x 160 |
A x b1 | 9 x b0 |
Equivalent Decimal Representation | |
160 | 9 |
The Obvious Pattern
The pattern here should be obvious. In number representations each digit represents that amount multipled by the base raised to the position of the digit from the right, starting from zero.
Sums
A further pattern to be noticed is that the maximum number that can be represented by n digits is 1 less than the smallest number that can be represented by n + 1 digits. As an example, the largest decimal number that can be represented by three digits is 999. This is exactly one less than 1,000, which is the smallest number than can be represented by four digits.
Determining Representation
So far I've looked at how to understand number representations in terms of decimal, but many people find it more challenging to go the other way, particularly in an organized, algorithmic fashion. But really, there are only two mathematical concepts a person needs to understand: integer division and modulus.
In integer division we disregard any remainder.
Numerator | Denominator | Result |
---|---|---|
4 | 3 | 1 |
6 | 7 | 0 |
16 | 5 | 3 |
Modulus on the other hand is all about the remainder.
Numerator | Denominator | Result |
---|---|---|
4 | 3 | 1 |
6 | 7 | 6 |
16 | 5 | 1 |
4 | 2 | 0 |
Let's consider a simple conversion: represent the decimal number 1 in binary.
We'll get the result of 1 mod b where b = 2. Since we want position zero, this becomes 1 mod 2, which we know must be 1. We'll then divide 1 by 2 and get zero. Since we have zero remaining, we don't need any further digits to represent 1 in binary.
Let's consider a slightly more complex example.
Representing decimal number 234 in binary | |
---|---|
Decimal | Modulo 2 |
234 | 0 |
117 | 1 |
58 | 0 |
29 | 1 |
14 | 0 |
7 | 1 |
3 | 1 |
1 | 1 |
0 | 0 |
Final result = 11101010 |
Programming
When we consider the repetitive nature of this process, it should be quite easy to map it to code in a programming language using either procedural iteration or recursion.
Python 3.0 Example
>>> def to_binary(n): ... digits = [] ... while n > 0: ... digits.insert(0, n % 2) ... n //= b ... return digits ... >>> to_binary(4) [1, 0, 0] >>> to_binary(234) [1, 1, 1, 0, 1, 0, 1, 0]
Making it more generic
This algorithm doesn't need to specify the base at all.
More Generic Python 3.0 Example
>>> def to_base(n, b): ... digits = [] ... while n > 0: ... digits.insert(0, n % b) ... n //= b ... return digits ... >>> to_base(4, 2) [1, 0, 0] >>> to_base(234, 2) [1, 1, 1, 0, 1, 0, 1, 0]
(Tail) Recursive O'Caml Example
Objective Caml version 3.11.0 # let to_base n b = let rec helper n acc = if n = 0 then acc else helper (n / b) ([n mod b] @ acc) in helper n [];; val to_base : int -> int -> int list = <fun> # to_base 4 2;; - : int list = [1; 0; 0] # to_base 234 2;; - : int list = [1; 1; 1; 0; 1; 0; 1; 0] #