Numeric Base Conversion
From LinuxServerTech
Discussion
Sometimes you need to read a number using a different base, ie base 16 or such. While there are various routines which are much faster than this code built into many libraries, a generic "read a base x number" and/or "write a base x number" routines are difficult to find.
Following is a Perl implementation with a few added quirks. For one thing, it allows you to define the characters used to display a value in a given digit position. In Base 16, we are used to using 0-9 for our "normal" values of 0-9 (taken from our Base 10 we learn growing up), and using A-F to represent the other values that can be in a particular position, ie the base 10 values 10-15.
However, when doing something strange like base 26, it is common practice to use the characters a-z to represent the values 0-25 which can appear at any position.
The Code
#! /usr/bin/perl -w
# used to determine the characters used to display a number
# Create a string with one character per possible value in a digit
# of the base in question. Split on // to make an array of values
my %baseDigits;
# note, this charset is good for any base UP TO 16
# if you don't know why, think about it a little bit
$baseDigits{16} = [split( //, '0123456789abcdef')];
# for base 26, we use the alpha's, so a 0 will be displayed as an 'a'
# with a value of 25 for any digit being displayed as a 'z'
$baseDigits{26} = [split( //, 'abcdefghijklmnopqrstuvwxyz')];
# Subroutine decodes a string representation of a number (value)
# in a given base, optionally using the digit definition passed as the final
# array in the parameter list.
# returns the numeric value the string
sub decodeBaseNum {
my ($value, $base, $digitDefinitions ) = @_;
$digitDefinitions = $baseDigits{16} unless $digitDefinitions;
# convert digits to hash using digit char as the key and value as the integer value
my $i = 0;
$digitDefinitions = { map { $_ => $i++ } @$digitDefinitions };
my $result = 0;
foreach my $digits ( split( //, $value ) ) {
$result = $result * $base + $$digitDefinitions{$digits};
}
return $result;
}
# given a number (value), encodes the number as a base something number,
# optionally using a set of digits defined by the caller
sub encodeBaseNum {
my ( $value, $base, $digitDefinitions ) = @_;
$digitDefinitions = $baseDigits{16} unless $digitDefinitions;
my $result = ;
while ( $value ) {
$result = $$digitDefinitions[$value % $base] . $result;
$value = int($value / $base );
}
return $result;
}
print 'ff base 16 is ' . &decodeBaseNum( 'ff', 16 ) . "\n";
print "1024 in base 16 is " . &encodeBaseNum( 1024, 16 ) . "\n";
print "1024 in base 16 using base 26 digits is " . &encodeBaseNum( 1024, 16, $baseDigits{26} ) . "\n";
$code = 'bba';
$num = &decodeBaseNum( $code, 26, $baseDigits{26} );
print "$code in base 26 is $num\n";
$num++;
print "$code plus one is $num or " . &encodeBaseNum( $num, 26, $baseDigits{26} ) . " in base 26\n";
1;
Caveats
- There is no error checking in this code, so if you tell it to create a base 26 number using the base 16 charset, you will get very weird results
- Round off errors can cause incorrect values if you exceed Perl's maximum number of valid digits
