Tutorial on number conversion, any base

Having seen many postings that request algorithms and programs to

convert decimal to binary, octal or hex and vice versa, here is a

back to basics approach that should allow anyone to convert ANY

base to ANY other base. Part of the confusion results from the

computer showing number values and number symbols just the same.

When you enter the symbols '123', unless forced, you do not

think:

The value of '123' is (1 * 10^2) + (2 * 10^1) + (3 * 10^0).

However, that is the way ALL single base number systems work.

(Roman numerals are not single based and relate to abacus like

calculation.) Base to the 0 power always has a value of 1 and is

always in the rightmost position. Each symbol position to the

left has a position value of the base times the position value

to its right. Since the rightmost is always 1, it is easy to

progress to the left and find all the position values.

In binary (base 2) this becomes:

..... 2^4 2^3 2^2 2^1 2^0

position value-- 16 8 4 2 1

In hex (base 16) this becomes:

..... 16^4 16^3 16^2 16^1 16^0

position value--65636 4096 256 16 1

or in general:

..... Base^3 Base^2 Base^1 Base^0

position value -- -- Base 1

A feature that can be used for error checking is that no digit

symbol can have a value equal to or greater than the base. For

EVERY base the symbol string representing the base value is '10'.

For bases 10 or less we could use something like the VAL command

of Turbo and QBasic, however, in hex the symbols above '9' would

not work. By convention, we use alphabetic characters as numeric

symbols when the base exceeds 10. That can be done even for bases

greater than 16 if you choose. Given that A = 10, B = 11..F = 15,

that completes what you need to convert numerical symbol

strings representing ANY base, 2..16, to their integer values.

Here is a program fragment:

VAR

SymbolStringIn:String; (* alpha-numeric string to convert *)

n, BaseIn:Integer;

PosValue, IntVal:LongInt;

NumSymbol:Char;

...............

Begin

n := Length(SymbolStringIn); (* "While" loop counter *)

IntVal := 0; (* becomes the value of SymbolStringIn *)

PosValue := 1; (* the base^power value *)

While n > 0 do

Begin

NumSymbol := NumStrIn[n]; (* extract rightmost char *)

IntVal := IntVal + (SymVal(NumSymbol) * PosValue);

Dec(n); (* move left one char *)

If n > 0 then PosValue := PosValue * BaseIn;

End;

End.

The programmer supplied Function SymVal (not shown) returns the

digit char value, i.e. 0 for '0', 2 for '2', 10 for 'A', 12 for

'C' and so on. The last "If n > 0" conditional keeps PosValue as

low as possible in an attempt to avoid out of range numbers for

the computer. You still need other safeguards if you intend to

use numbers which approach the maximum value of LongInt.

Going from an integer to a new base still uses the same

knowledge-- we just go backwards. First, let's review how you

parse an integer into its separate digits with the integer 345:

(1) Rightmost digit is 345 Mod 10 giving 5

(2) Stripping the 5 away to leave the rest is done by:

345 Div 10 giving 34

Repeat (1) and (2) until done.

ANY base works the same, just put the value of the new base where

10 is above. However, to display the answer, you convert each

digit to a symbol representing that digit's value.

VAR

SymStringOut:String;

IntVal:LongInt;

BaseOut, DigitVal:Integer;

Ch:Char;

.............

Begin

SymStringOut := '';

While IntVal > 0 Do (* value still to be converted *)

DigitVal := IntVal Mod BaseOut; (* rightmost digit *)

Ch := DigitSymbol(DigitVal);

NumStringOut := Concat(Ch, NumStringOut);

IntVal := IntVal Div BaseOut; (* remove previous digit *)

End;

End.

The programmer supplied Function DigitSymbol (not shown) returns

the symbol corresponding to the DigitVal.

With these facts you can convert any base to any other base by

first converting the input symbol string (base in) to an integer

value and convert that value to the output symbol string (base

out). By setting the input and output bases equal, you can

perform arithmetic on the value and make it look like you are

doing addition in binary, subtraction in hex, etc.

Under separate posting is a conversion program of wider utility

than most will ever need covering bases from 2 to 72. In addition

to the basics above it adds range checking to avoid over range

errors.

If you use a really old version of Pascal that does not include

the dynamic TYPE STRING, changes must be made. You can represent

the symbol strings as TYPE "Array [0..32] of Char" and use the

zero'th element to store the length as Chr(length). The length is

extracted with ord( ). No delete, insert or copy is used BUT, as

shown above, concantenation is. One way to do concantenation

with the added char going to the front of the string (as done

above) is to put the chars in from array element 1 on up and keep

the length byte up to date. This stores the chars in reverse

order but, since you know the length, it is easy to write the

output string correctly by writing the char array in reverse:

For n := Ord(A[0]) downto 1 do Write(A[n]);

Writeln;

Hope you found this useful,