Welcome, guest | Sign In | My Account | Store | Cart

One function to rule them all....

Python, 79 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import string

def rebase(i, frombase=None, tobase=None, fromalphabet=None, toalphabet=None, resize=1, too_big=40000, debug=False):
    ''' if frombase is not specified, it is guessed from the type and/or char in i with highest ord.
        tobase defaults to [10, 2][frombase == 10].
        the alphabets are map(chr, range(256)) if its base is between 62 and 255;
        otherwise, string.digits+string.letters.
        always returns a string which is also valid input.
        valid bases are ints in range(-256, 257).
        alphabets must be subscriptable, and can only contain str's.
        invalid tobases are replied with 'why?'; rebase('why?') == '217648673'.
        returned string is zfilled to the next largest multiple of resize
    '''
    if frombase == None:
        if isinstance(i, (int, long)):
            frombase = 10
        elif isinstance(i, str):
            a = str(i)
            if any([(chr(x) in a) for x in range(ord('0')) + range(58, 65) + range(91, 97) + range(123, 256)]):
                frombase = max(map(ord, a)) + 1
            else:
                frombase = max(map((string.digits + string.letters).index, a)) + 1
    if tobase == None:
        tobase = [10, 2][frombase == 10]
    # got bases, ensuring that everything is an int
    tobase = int(tobase)
    frombase = int(frombase)
    abstobase = abs(tobase)
    absfrombase = abs(frombase)
    if absfrombase in [0, 1]:
        i = len(str(i))
    elif 2 <= frombase <= 36:
        # may be difficult to translate to C
        i = int(str(i), frombase)
    else:
        i = str(i)
        n = 0
        if fromalphabet == None:
            if 62 <= absfrombase <= 256:
                fromalphabet = map(chr, range(256))
            else:
                fromalphabet = string.digits + string.letters
        fromalphabet = fromalphabet[:absfrombase]
        for j in range(len(i)):
            n += (frombase ** j) * fromalphabet.index(i[-1-j])
        i = n
    # got ints, converting to tobase
    if debug: print 'converting %d from base %d to %d' % (i, frombase, tobase)
    if abstobase in [0, 1]:
        return '0' * ((i > 0) and int(i) or 0)
    elif abstobase > 256:
        return 'why?'
    # if execution gets here, we might want the result to be zfilled to a multiple of resize
    r = ''
    if tobase == 10:
        r = str(i)
    else:
        if i < 0:
            print 'negative',
            i = -i
        if toalphabet is None:
            if 62 <= abstobase <= 256:
                toalphabet = map(chr, range(abstobase))
            else:
                toalphabet = (string.digits + string.letters)[:abstobase]
        if tobase < 0:
            i = -i
        j = 0
        while i != 0:
            r = toalphabet[i % tobase] + r
            i /= tobase
            j += 1
            if j >= too_big: raise "call again; set too_big bigger"
    if resize > 1:
        if 62 <= abstobase <= 256:
            r = toalphabet[0] * (resize - (len(r) % resize)) + r
        else:
            r = r.zfill(len(r) + resize - (len(r) % resize))
    return r

I was tired of collecting cute lambdas to convert among decimal, octal, binary, hex, and wanted one function to handle every base I could think of [including 256 and -256].

Negative numbers are a known issue. I suggest coupling a rebase string with a bool if you need negative number support for extraordinary bases.

1 comment

Beau Martínez 11 years, 8 months ago  # | flag

A much less heavyweight version is:

def rebase(number, from_alphabet, to_alphabet):
    if not isinstance(number, str):
        raise TypeError('The number must be a string.')

    if len(from_alphabet) < 2 or len(to_alphabet) < 2:
        raise ValueError('Alphabets must have at least two symbols.')
    if sorted(set(from_alphabet)) != sorted(from_alphabet) or sorted(set(to_alphabet)) != sorted(to_alphabet):
        raise ValueError('Alphabets cannot have any repeated symbols.')

    to_base = len(to_alphabet)
    from_base = len(from_alphabet)

    denary_number = 0

    for i in range(len(number)):
        denary_number += (from_base ** i) * from_alphabet.index(number[-(1 + i)])

    arbitrary_base_number = ''

    while denary_number != 0:
        arbitrary_base_number = ''.join([to_alphabet[denary_number % to_base], arbitrary_base_number])
        denary_number //= to_base

    return arbitrary_base_number
Created by faulkner612 on Tue, 18 Jul 2006 (PSF)
Python recipes (4591)
faulkner612's recipes (2)

Required Modules

Other Information and Tasks