Monday, April 20, 2009

Latin numbers a.k.a. alpha numbers a.k.a. alphabetical Excel column naming/numbering in Java

There is that kind of odd "number-system" or number formatting that Excel uses for the columns. I believe this is also used for "numbering" appendices in books. At the first glance you will think it's just a 26-based number system, but it's not, as this system has no 0 digit, which means you will miserably fail with the good-old division-and-modulo-with-the-base trick. Anyway, here it is, methods for converting int to Latin number, without upper limit. Watch out, in this implementation A is 1, not 0. Public Domain:

/**
 * Converts a number to upper-case Latin (alpha) number, like
 * A, B, C, and so on, then Z, AA, AB, etc. No upper limit.
 */
public static String toUpperLatinNumber(int n) {
    return toLatinNumber(n, 'A');
}

/**
 * Converts a number to lower-case Latin (alpha) number, like
 * a, b, c, and so on, then z, aa, ab, etc. No upper limit.
 */
public static String toLowerLatinNumber(int n) {
    return toLatinNumber(n, 'a');
}

private static String toLatinNumber(final int n, char oneDigit) {
    if (n < 1) {
        throw new IllegalArgumentException("Can't convert 0 or negative "
                + "numbers to latin-number: " + n);
    }
    
    // First find out how many "digits" will we need. We start from A, then
    // try AA, then AAA, etc. (Note that the smallest digit is "A", which is
    // 1, not 0. Hence this isn't like a usual 26-based number-system):
    int reached = 1;
    int weight = 1;
    while (true) {
        int nextWeight = weight * 26;
        int nextReached = reached + nextWeight;
        if (nextReached <= n) {
            // So we will have one more digit
            weight = nextWeight;
            reached = nextReached;
        } else {
            // No more digits
            break;
        }
    }
    
    // Increase the digits of the place values until we get as close
    // to n as possible (but don't step over it).
    StringBuilder sb = new StringBuilder();
    while (weight != 0) {
        // digitIncrease: how many we increase the digit which is already 1
        final int digitIncrease = (n - reached) / weight;
        sb.append((char) (oneDigit + digitIncrease));
        reached += digitIncrease * weight;
        
        weight /= 26;
    }
    
    return sb.toString();
}

1 comment: