Most of the modern world uses a base-10 number system. It's the easiest to work with. However, when it comes to computer systems, we use a few different number systems too. Base-2, or binary, is the basis of data storage and transmission; a bit is either on or off. We also use Base-8, or octal; each byte contains 8 bits. Last but not least is Base-16, or hexadecimal.
Under some circumstances, we might need to convert between these different number systems. ASP provides a pathetic two functions for this purpose: hex(), which converts from base-10 (decimal) to base-16 (hexadecimal), and oct() which converts from decimal to octal. PHP provides equivalent functions dechex() and decoct(), as well as additional functions decbin() for converting from decimal to binary, bindec() for converting from binary to decimal, hexdec() for converting from hexadecimal to decimal, and octdec() for converting from octal to decimal.
It would be nice to extend ASP to support these four additional functions. When I started doing that, I noticed a pattern in the formulae. All the functions that converted from decimal were fairly similar, and all the functions that converted to decimal were also fairly similar. So I abstracted the calculations out into their own functions and simplified my code.
ASP
' Convert from binary to decimal.
function bindec(bin)
bindec = toDecimal(bin, 2)
end function
' Convert from decimal to binary
function decbin(dec)
decbin = fromDecimal(dec, 2)
end function
' Convert from decimal to hexadecimal.
function dechex(dec)
' Assume that built-in hex() function is faster.
dechex = hex(dec)
end function
' Convert from decimal to octal.
function decoct(dec)
' Assume that built-in oct() function is faster.
decoct = oct(dec)
end function
' Convert from hexadecimal to decimal.
function hexdec(hex)
hexdec = toDecimal(hex, 16)
end function
' Convert from octal to decimal.
function octdec(oct)
octdec = toDecimal(oct, 8)
end function
Before we get into the underlying fromDecimal() and toDecimal() functions, some things need to be said. The lowest possible base is 2, so if the user tries to specify a base smaller than 2, we will change it to 2. Likewise, there is a practical upper limit of base 36. Beyond that, things get more complicated, so any base higher than 36 will be changed to 36 to prevent the function from going out of bounds and returning bad data.
ASP
function toDecimal(value, radix)
dim result
dim digit
result = 0
' Prevent radix from going out of bounds.
if radix < 2 then
radix = 2
elseif radix > 36 then
radix = 36
end if
for i = 1 to Len(value)
digit = Mid(value, i, 1)
' Convert letters to numbers.
if not isNumeric(digit) then
' The letter A in any base is equal to 10 in decimal.
' The ASCII value of A is 65, so subtract 55 from the ASCII value to obtain the decimal value.
digit = Asc(UCase(digit)) - 55
else
digit = CInt(digit)
end if
' Return zero if any digit is out of bounds for the radix.
if digit >= radix then
result = 0
exit for
end if
result = result + (digit * radix ^ (Len(value) - i))
next
toDecimal = result
end function
function fromDecimal(value, radix)
dim result
dim digit
result = 0
' Prevent radix from going out of bounds.
if radix < 2 then
radix = 2
elseif radix > 36 then
radix = 36
end if
' Check for invalid input.
if isNumeric(value) then
' Inputted value appears to be base 10. OK to proceed.
do until value = 0
digit = value Mod radix
if digit > 9 then
digit = Chr(digit + 55)
end if
result = CStr(digit) & result
value = value \ radix
loop
else
' Inputted value was NOT base 10.
result = 0
end if
fromDecimal = result
end function
It would be even better if we could easily convert between two bases where neither one is decimal. Also, it would be nice to have a unified interface. We can achieve both with one function, base_convert(), which is also present in PHP.
ASP
function base_convert(value, sourceRadix, targetRadix)
if sourceRadix = targetRadix then
' If source radix and target radix are equal, don't waste time converting.
baseConv = value
elseif sourceRadix = 10 then
' If source radix is decimal, skip converting to decimal.
baseConv = fromDecimal(value, targetRadix)
elseif targetRadix = 10 then
' If target radix is decimal, skip converting from decimal.
baseConv = toDecimal(value, sourceRadix)
else
' Convert to decimal, and then from decimal.
baseConv = fromDecimal(toDecimal(value, sourceRadix), targetRadix)
end if
end function
I leave you with the following potential exercises:
- From base 37 to 62, the lowercase letters of the Latin alphabet are used, but special handling would need to be added for them. The ASCII value for Z is 90. The ASCII value for a is 97. There are six other characters in between.
- Base64 encoding begins with the uppercase letters A-Z, followed by a-z, followed by 0-9, followed by + and /. If you want to add Base64 support, separate functions would be wise, but could still be tied into the base_convert() wrapper.
2 comments:
There is a bug in the toDecimal function - try caling toDecimal("8A", 36) and get 0 back.
The issue is you do not convert a numeric digit to an Integer, so VBScript does a string comparison and "8" is greater than "36".
I suggest adding an else to the if not isnumeric:
else
digit = CInt(digit)
Thanks, I've updated the code both here and on Snipplr.
Post a Comment