digit
At first, this is necessarily very confusing. Understand that in each base, there is a maximum value that each digit can hold before the number must use the next highest digit. In base 10, which seems almost natural to us, the maximum value that can be displayed in a digit is 9.
Now to explain the example above. 100 in base 16 is 256 in base 10 because base 16 (as its name implies) displays more values in each digit than decimal does. Because it displays 6 more values than decimal in each digit, base 16 can display the same value with lesser and often fewer numerals.
You may be wondering now, what base 16 displays those extra numerals as since there are no more numerals to use after 9. This is something we'll get to in the next section.
Base | Name | Characters |
---|---|---|
2 | binary | 0-1 |
3 | ternary | 0-2 |
4 | quaternary | 0-3 |
5 | quinary | 0-4 |
6 | senary | 0-5 |
7 | septenary | 0-6 |
8 | octal | 0-7 |
9 | nonary | 0-8 |
10 | decimal | 0-9 |
11 | undenary | 0-a |
12 | duodecimal | 0-b |
13 | *tridecimal | 0-c |
14 | *quattuordecimal | 0-d |
15 | *quindecimal | 0-e |
16 | hexadecimal | 0-f |
17 | septendecimal | 0-g |
18 | *octodecimal | 0-h |
19 | *nonadecimal | 0-i |
20 | vigesimal | 0-j |
Note that although these names are based on common Latin prefixes and roots, they are not all standard names. The asterisks are used to mark the names that are more invention than standard. Hexadecimal, while being fairly standard, is not the correct name for base 16 according to the Latin system. Had sensibly-minded people not intervened, the name would have been something like sexadecimal, but the problem there is obvious.
As you can see in the table, letters are used after the numerical character 9. This is also an invention; letters were chosen to represent numbers because they were easily typed and easily displayed by computers.
Also apparent here is that each base name is one greater than its highest digit value. For example, base 2 does not use a '2'. This is because each base starts designating numbers at 0. Base 2 is called base 2 because it can display 2 values per column.
Since we must use a base when displaying, or if the number will use two digits, speaking a number, we use decimal. This is how each base is named (e.g., base 16) - according to the decimal display of the number of values per digit that the base uses.
So keep in mind that base 16 wouldn't be called that if everyone used it. Because in base 16, the decimal 16 would be displayed as 10.
Some people go overboard with base 10 - creating the impression that the "real" version of a number is its decimal display. This is in the author's opinion a bad mistake to make. You should take care to note which base a number is in when working with bases other than decimal.
But here's the rub: In mathmatics, you'll carry over to the next digit after you cannot display anything greater in that digit with the base you're using. But you won't when counting on your fingers. You'll display 10 by holding out all of your fingers - but this would be the same as holding out one finger if you were counting in tens.
So in decimal math, there is no inbetween '10' character for the first digit. You'll get to 9 and then carry over. But when counting on your fingers, you'll get to 9, and then you can carry over or hold out your tenth finger. And holding out all of your fingers displays the same number as does holding out one finger when counting tens.
Why? Because base 16 has to count through more digit values before it carries over to the next digit. For example, one less than 100 in base 16 is FF. Decimal does not display so many values per digit, so it carries over when it reaches 9. Hexadecimal, on other hand, goes up to F before it carries over to the next greater digit. This helps us to understand why the digits are worth more in higher bases - they are bought at a higher price, so to speak.
Base | Digit 0 | Digit 1 | Digit 2 |
---|---|---|---|
Binary | 1 (20) | 2 (21) | 4 (22) |
Octal | 1 (80) | 8 (81) | 64 (82) |
Decimal | 1 (100) | 10 (101) | 100 (102) |
Hexadecimal | 1 (160) | 16 (161) | 256 (162) |
As you can see, the decimal value of each digit in a base is determined by finding baseNum to the power of digitNum. For example, if baseNum were 16 for hexadecimal, and digitNum were 2 for the third digit, the decimal eqivalent of a 1 in digit 2 would be 256. That is, the value of a 1 in hexadecimal's digit 2 would be the same as 256 in decimal. This provides us with a tool for conversion to and from decimal.
But remember that we're using decimal to do the math so the conversion can only be used with decimal numbers. If we were to use other bases with this conversion system, we would have to make another table.
When converting from one base to another, decimal (being ingrained) will be the base that is used to do the math. So you'll be converting from one base to decimal and then to another base if need be.
First, we'll do some simple conversions from octal to decimal using the chart above.
Convert the octal number 25 to decimal: For digit 1: 2 * 8 = 16. Add 16 to our result. For digit 0: 5 * 1 = 5. Add 5 to our result. The result is: 21.In that example, we simply multiplied each digit by its decimal value from the chart above, and then added the products to find the result. Now let's convert back to octal.
Convert the decimal number 21 to octal: There are no 64's in our number. There are two 8's in our number. Add 20 to our result. There is a remainder of 21-16=5. There are five 1's left. Add 5 to our result. The result is: 25.In this last example, we went through each octal digit in our chart from greatest to least. We found two 8's in our number. Because we found these two 8's, we added two 10's to our result because 10 is decimal's value for digit 1 and the 8 is octal's value for digit 1. Now let's do a conversion from decimal to binary.
Convert the decimal number 13 to binary: There are no 16's in our number. There is one 8 in our number. Add 1000 to our result. There is a remainder of 13-8=5. There is one 4 in our number. Add 100 to our result. There is a remainder of 5-4=1. There are no 2's in our number. There is one 1 in our number. Add 1 to our result. The result is: 1101.That example used some binary digits not on our chart, but we know that the numbers for binary digits 3 and 4 are 8 and 16 respectively. Now let's convert back to decimal.
Convert the binary 1101 to decimal: For digit 3: 1 * 8 = 8. Add 8 to our result. For digit 2: 1 * 4 = 4. Add 4 to our result. No value for digit 1. For digit 0: 1 * 1 = 1. Add 1 to our result. The result is: 13.Now, we'll do a more complicated hexadecimal to decimal conversion in slightly different form.
Convert the hexadecimal 2EA.F to decimal: result = (2 * 162) + (14 * 161) + (10 * 160) + (15 * 16-1) result = (512) + (224) + (10) + (15/16) result = 746 + 0.9375 result = 746.9375Notice in this last example that a decimal point was used in the hexadecimal number. We'll get to floating-point numbers later on. Also, 16 to the power of -1 is the same as 1/16. Let's convert the number back to hexadecimal.
Convert the decimal number 746.9375 to hexadecimal: First the 746: There are two 256's in our number. Add 200 to our result. There is a remainder of 746-512=234. There are fourteen 16's in our number. Add E0 to our result. There is a remainder of 10. There are ten 1's left in our number. Add A to our result. The result is now: 2EA. Now the fractional portion: result2 = 9375/10000 result2 = 15/16 (reduced) There are fifteen 1/16's in our number. Add 0.F to our result. The result is 2EA.F.There are several important things to note in this example. Because a hexadecimal digit can hold a higher value than a decimal digit, having ten 1's and fourteen 16's is reasonable. Because fractions in hexadecimal are in sixteenths instead of tenths (as in decimal), 15/16 is the same as 0.F. It's just like having 9/10 in decimal - it's the same as 0.9.
Decimal | Binary | Hexadecimal |
---|---|---|
0 | 0 | 0 |
1 | 1 | 1 |
2 | 10 | 2 |
3 | 11 | 3 |
4 | 100 | 4 |
5 | 101 | 5 |
6 | 110 | 6 |
7 | 111 | 7 |
8 | 1000 | 8 |
9 | 1001 | 9 |
10 | 1010 | A |
11 | 1011 | B |
12 | 1100 | C |
13 | 1101 | D |
14 | 1110 | E |
15 | 1111 | F |
As you can see from the table, hexadecimal can represent the same value in much less space than binary and decimal. Also, each hexadecimal number is represented by four bits (a nybble). This makes hexadecimal useful for displaying memory values in an efficient form.
Hexadecimal is also useful for storing up to four boolean values in a digit. For example, any addition of 1, 2, 4, or 8 yields a number with known components. For example, 3=1+2, F=8+4+2+1, E=4+8+2, 9=8+1. Any combination of 1, 2, 4, and 8 results in a number that can be decomposed into the numbers that add up to it. If you look at the table above, you'll see why. It is this fact that Cog makes use of for its flag system.
One last thing about hexadecimal: it can be easily converted to and from binary in a much more simple method than the previous conversion examples used. Using the table above, you can convert a binary number of four bits to its hexadecimal form. So the number 11110011 is F3. Just seperate the binary number into nybbles and use the table above to find the hexadecimal representation for that binary number.
Octal is similar to hexadecimal in that it is used for some of the same reasons. It's maximum digit value is 7 which is represented in binary as 111. Only three bits are needed to store an octal digit. Like hexadecimal, it is easily converted to and from binary - instead of nybbles, use groups of three bits.
Binary is extremely useful to computers because values of 0 and 1 - true and false - are easy to store and transfer. The downside is that they are much less efficient in terms of the space that it takes to store and display so many digits. For example, 9 in decimal is 1001 in binary.
Often enough, you will need to use hexadecimal or octal numbers in your code - for convenience, efficiency, or what have you. Say you wrote in 100. When the interpreter/compiler/parser engine reads that, it will assume that the number is in decimal form. So if you really meant a hexadecimal 100, you would have made a grave mistake.
To tell the engine what form the number is in, there are two fairly-standard base prefixes which can be put before a number. These are '0x' for hexadecimal and '0' for octal. So if you want to give a hexadecimal 100, you would write 0x100. Or if you wanted to write 100 in octal, you would write 0100. Other bases will have to be converted to decimal. Note that not all compilers or interpreters support these prefixes. Cog, being based on C++, does support them.
A hexadecimal or octal number can have a decimal point just as base 10 numbers do. But, this is hardly ever done. And since there is really no reason to have a floating-point number in hexadecimal or octal notation, other bases are usually restricted to being integers - they must be whole numbers. In fact, not even the Windows calculator allows floating-point numbers in non-decimal bases.