Base Conversion Functions

In the theme of URL shorteners, I whipped up some simple base conversion functions that don't have the hard limit of 36 that the inputBaseN and formatBaseN built-ins do, but otherwise are exact replacements.  Everything is controlled by the first line, the list of digits.  The functions will support whatever base you want, as long as you can supply enough unique digits.  Note that not all characters are valid on the URL, so for URL shortening you have to be somewhat careful what you pick.

The digit list below is 62 long.  You could add underscore, dash, period, plus, exclamation point, asterisk, apostrophe, left paren, right paren and comma to the list with relative safety, bringing the max base up to 72 URL-safe digits.  Depending on your webspace manager, you might be able to add slash and backslash.  And if you don't care about URLs, just shortening something, you can add pretty much anything.

Both functions will return the string "NaN" for an unsupported (too large or too small) base or an invalid number.   This is because I wrote them in CFSCRIPT and when I went to add bound checking I realized there still isn't a 'throw' statement in CFSCRIPT so I punted.  Write to Adobe.

The code should run on any CFML engine that supports CFSCRIPT UDFs.  I tested on either CF8 or Railo 3, but I'm not sure which.

digits = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
function decimalToBase(num, base) {
    var result = "";
    if (base LT 2 OR base GT len(digits)) {
        return "NaN";
    }
    result = mid(digits, num MOD base + 1, 1);
    while (num GT base) {
        num = num / base;
        result = mid(digits, num MOD base + 1, 1) & result;
    }
    return result;
}
function baseToDecimal(num, base) {
    var result = 0;
    var digit = "";
    var pos = "";
    if (base LT 2 OR base GT len(digits)) {
        return "NaN";
    }
    while (len(num) GT 0) {
        digit = left(num, 1);
        num = removeChars(num, 1, 1);
        pos = find(digit, digits);
        if (pos LTE 0) {
            return "NaN";
        }
        result = result + (pos - 1) * base ^ len(num);
    }
    return result;
}

5 responses to “Base Conversion Functions”

  1. John Farrar

    You could also add an underscore. Isn't that allowed in all URLs/file names? We have used this concept in a number of areas. Your script is cleaner than one we created years ago… thanks for sharing.

  2. Tolaneas

    If CFScript doesn't support a throw statement, you can always write a function in a CFC using CFML that takes arguments to pass to a CFThrow tag.

    THEN you could call the CFC method in CFScript to throw yourself an error.

  3. PB

    Not sure if anyone is still active in this forum…

    But I the logic in this code (great, clean code!) and converted it to plpgsql and ran some tests. It works great except I ran into an error.

    When trying to convert "10184397971067″ from base10 to base2 (DEC2BIN), I get the result of:
    "0010100001100111101011011000101111001111011″
    where I should have gotten:
    "10010100001100111101011011000101111001111011″ (the left-most "1″ is missing).

    I verified this with MySQL CONV function, which got the result of "10010100001100111101011011000101111001111011″.

    Can someone verify this?