Numeric Bases and Defining Your Own
Lately I’ve been teaching my niece a new way to do math. She would have difficulty with addition and subtraction of numbers if they contained more than a few digits. I asked her if she’d ever seen a clock where the numbers rolled over (like in the movie Ground Hog Day). She hadn’t. I decided to describe numbering with terms similar to the way this kind of clock works; digits and roll-overs. If you can understand how when a single digits place gets to its highest number and then “rolls over” the next digits place by one, then you can easily add or subtract any length of number. Not only that but it’s an excellent way to prepare some one to learn alternative numbering systems that work the same way like binary or hexadecimal. These numbering systems are known as Positional Notation, or Place-Value Notation. Numbering systems that this won’t work with are non-positional notations such as Roman Numerals or the Tally system.
Base 10 Primer
First thing’s first. How many single digits are there? I cut out four thin strips of paper which I wrote on and numbered from the top to the bottom; nine down to zero. I then cut out a rectangle window out of another sheet of paper just tall enough to show one digit, and then marked above the window “THOUSANDS | HUNDREDS | TENS | ONES”. From there we are able to slide the strips of paper up and down in each position to represent the single digit that belongs to each position. When asking my niece how many single digits there were, I defined what a digit was, she answered me four. She was confusing the paper cut out display for the question. Asking her how many single numbers where below ten she said nine. I then explained to her that zero is a number and had to write it down, put it in a box, and show that that was one digit on it’s own. Then I asked her what nine plus one was. She said ten. So now it’s starting to sink in. When asking her how many single digits there were, she reverted back to her answer of four. So I then went on to tell her that all of her fingers on her hands are single digits. How many single digits are there? She got it; “ten”. I also had to explain the pinky could represent a zero; she thought that made sense since it was so small it was almost nothing. And from there things got easier.
We have standardized on base 10 being our primary way of expressing numbers and we refer to other number bases with a base 10 naming scheme. You may have heard of base 64; base 64 is sixty-four single digits long, represented as unique characters, with the included zero value being one of those digits. We could just as easily refer to it by another name; in binary it would be base 1000000 and in Ruby we we write that as 0b1000000. We could even name it by octal, hex, or a made up numbering system. I say this just to illustrate that numbers are a representative language. We are most comfortable with discussing things in base 10 because that is what we have learned and were taught. This numeric base is a language for numbering that has crossed over easily into almost all other written languages. It’s the most accepted language for math. And mathematics is the “only” exact science. Programming is made possible because of this. From machines built on ones and zeros, on or off, up to higher level programming languages designed to work with our own preferred numeric base 10 representation.
The numeric bases we use all throughout programming and mathematics are primarily positional notation. What I’ve explained earlier is an illustrative method for positional notation. The zero value in positional notation has both a value of nothing and works as a positional place holder. Without this zero value we wouldn’t have positional notation. We’d be using something like Roman Numerals or Tally Notation. When you reach the maximum single digit value for the position; you progress forward to the next number; the current position gets set to its zero value and the next number position up (to the left) gets bumped up one.
The value of each digit position is the value of its digit, multiplied by the power of the digit’s position to its base. So the first position, the ones position (or whatever represents one), would get multiplied by the number base to the power of zero.
Example: the number 4 in base 10 is equal to 4×100 (four times ten to the zeroth power) and 40 would be 4×101 and 400 would be 4×102. In Ruby you would write power of with two stars ** eg: 4*10**2. For octal numbers, base 8, 40 is not what you think it is (if you understood it as forty in base 10), it is 32 (in base 10), you can represent it in the same way 4×81 and in Ruby 4*8**1.
Lets use a binary word position comparison. Just like I wrote “thousands, hundred, tens, ones” for base 10, for binary those first four would be represented as “eights, fours, twos, ones”. You can see it compounds its value times itself (positional name) as it continues up to the left. After the eights position you have the sixteens position. Why did I write sixteens and not sixteenth? Because it’s multiples of 16, it is not the 16th positions over. So when you see the binary number in Ruby 0b10000 you know it’s 16 in base 10 because 1 is in the sixteens position. Likewise in octal positions it would be as follows “five-hundred-and-twelves, sixty-fours, eights ,ones” and 64 in octal would be written in Ruby as 0o100 (read as zero-oh-one-zero-zero).
Now the standard bases that have been around a long while such as binary, octal, base10, and hexadecimal have a fixed set of characters set to represent each number. However higher base numbers such as base 64 have several different flavors, as it were, of characters set to define each single digit (the last two are what vary in base 64). So when you use higher numbering systems you need to be familiar with what standard you’re using. Useful resources that record these standards are Wikipedia and the RFC specifications. You may find details in documentation for the project(s) you are working with on which standard to use. Remember: numbering systems are in essence just another language, if you don’t agree on the language standard, or meaning, your going to run into compatibility issues.
In Ruby binary numbers start with 0b (zero-B), octals start with 0o (zero-oh), and hexadecimal start with 0x (zero-X). The number 256 can also be represented as 0b100000000, 0o400, 0x100. To convert an integer to any base up to base 36 you can use the .to_s method with an integer as the base number of what you want to convert to. And .to_i on a string will perform the same kind of base number conversion.
256.to_s(2) # => "100000000" "100000000".to_i(2) # => 256 256.to_s(8) # => "400" "400".to_i(8) # => 256 256.to_s(16) # => "100" "100".to_i(16) # => 256
So for binary you use base 2, octal base 8, and hexadecimal base 16. The way to_s implements converting bases for numbers greater than the hexadecimal standard is it just continues to use letters to represent single digit value positions. Hexadecimal started using the alphabet from A to F, so to_s just continues up until Z. Ten single digits of zero through nine and twenty-six characters of A through Z make thirty-six unique characters to represent value.
For any other standard numbering base you need to either implement how it works yourself, or use a library(gem) for it. The Ruby language includes a base 64 library for use. There are 6 methods provided with the library.
require 'base64' # => true Base64.methods - Object.methods # => [:encode64, :decode64, :strict_encode64, :strict_decode64, :urlsafe_encode64, :urlsafe_decode64] Base64.encode64("apple") # => "YXBwbGU=\n" Base64.strict_encode64("apple") # => "YXBwbGU=" Base64.urlsafe_encode64("apple") # => "YXBwbGU=" Base64.decode64("YXBwbGU=\n") # => "apple" Base64.strict_decode64("YXBwbGU=") # => "apple" Base64.urlsafe_decode64("YXBwbGU=") # => "apple"
Now base 64 is built with the ten individual single digit numbers, twenty-six lower case alphabet characters, twenty-six upper case alphabet characters, and two other characters. These two other characters are where different standards disagree on and on which you may need to check your standard by. See the Wikipedia on this: http://en.wikipedia.org/wiki/Base64
Base 62 is an agreed upon standard (to my knowledge) as it’s just all alpha numeric characters in order. Base 58 is a number system using characters that removes one of each character that may be confused for another. Base 58 has several variations on its standard: http://en.wikipedia.org/wiki/Base58
Write Your Own
With this much knowledge it should be fairly easy to invent your own numbering system. But I’ve got great news for you! I’ve already written a gem for defining your own numeric base. It’s called BaseCustom. Just specify the characters in the order of the value you would like them to have and it creates a mapping to convert your numbers. For example.
require 'basecustom' binary = BaseCustom.new('01') binary.base 400 # => "110010000" binary.base "110010000" # => 400 octal = BaseCustom.new('01234567') octal.base 32 # => "40" 2.2.0 :059 > octal.base "40" # => 32
As you can see I’ve written it so you only need to use one method to convert back and forth from the numeric base. The method .base will take a string and use the base that you implemented and convert it to a base 10 integer. To convert a base 10 integer to the base you’ve defined you just give the .base method the integer value. If you wanted to give it any other value you can simply write it with something that converts it to base 10. For example.
15.to_s(16) # => "f" "f".to_i(16) # => 15 hexadecimal = BaseCustom.new ("0".."9").to_a + ("a".."f").to_a hexadecimal.base "f" # => 15 hexadecimal.base 15 # => "f" hexadecimal.base 15.to_s(16) # => 15
On the last line 15.to_s(16) first converted 15 to a base of 16, which is a hexadecimal conversion. After 15 was converted to hexadecimal it was represented by the character “f”. “f” was then the value that hexadecimal.base then converted back to base 10 which is the number we started with 15.
Let’s make our own. Lets call it base 3 and we’ll use the unique characters A,B,C to represent value for each single digit character.
base3 = BaseCustom.new("ABC") base3.base 0 # => "A" base3.base 1 # => "B" base3.base 2 # => "C" base3.base 3 # => "BA" base3.base 4 # => "BB" base3.base 5 # => "BC" base3.base 6 # => "CA" base3.base 33 # => "BACA" base3.base 34 # => "BACB" base3.base "BACB" # => 34
Congratulations! We now have a new language in which we express values. This may seem cryptic, but that’s only because it’s not something we’re familiar with yet. Not until you see it as the positional notation that it is and you know its ordering. Then it all makes sense. One really cool thing about this is you can “encrypt” messages by having all characters represented as a number and convert it to base 10. The order of the characters is important as that could be considered the “key” to decrypt it. If you wanted to obfuscate it you could run it through several different kinds of number base conversions with varying orderings. But I won’t be getting into the how of that now.
Now there are times when we may want to use more than one character to represent the value of a single digit. In that case Ruby needs to know how to tell them apart. We will most likely need a separator for them known as a delimiter. A delimiter will be to your numbering system as space is to your sentences. Let’s use music chords for our numbering system and a space character for our delimiter.
bMusic = BaseCustom.new(["\n","A","A#","B","C","C#","D","D#","E","F","F#","G","G#"], " ") bMusic.base "E A C D G B" # => 3008853 bMusic.base 3008853 # => "E A C D G B " bMusic.base "E C G D \n A D G \n E C G D \n F A# A" # => 5573207572890438196 puts bMusic.base 5573207572890438196 # E C G D # A D G # E C G D # F A# A
With this you can secretly send chord progression charts to your friends just by sending them the number. As long as they have the order of characters known they can use BaseCustom to convert to, and from, base 10 (or any other base) to get the original message.
We all use numbers. Understanding them and how they work make them fun to work with. We all speak languages; mathematics is a language, as well as the numbering systems which each represent meaning of value. You speak base ten? You’re speaking my language ;-). Now where mathematics is definitely a language, I must admit I’m not sure numeric notations are a language in and of themselves… that a bit more technical than I know.
Hopefully this will give you a better understanding of how to use numbers and how to teach them. I’m fully enjoying teaching my niece in this way. It takes the complication out of big numbers. Things tend to be much simpler when you break them down to smaller and easier to comprehend components.
-Daniel P. Clark
Image by Seán Ó Domhnaill (Soilse) via the Creative Commons Attribution-NonCommercial-NoDerivs 2.0 Generic License.