ActiveState Code

Recipe 65212: Convert from decimal to any base number


This function takes in any base-10 integer and returns the string representation of that number in its specified base-n form.

Up to base-36 is supported without special notation for symbolizing when a number is really in a single digit position. When that does occur, the number that takes up that single base is surrounded by parantheses.

[2004-06-30: Renamed function to base10toN to be more proper]

[2001-06-17: Changed comments to base-36 instead of base-35; thanks Klaus Alexander Seistrup]

[2001-06-17: Added for loop mechanism in Discussion for alternative way of creating num_rep dictionary; thanks Hamish Lawson for suggesting that possibility]

Python
 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
def base10toN(num,n):
    """Change a  to a base-n number.
    Up to base-36 is supported without special notation."""
    num_rep={10:'a',
         11:'b',
         12:'c',
         13:'d',
         14:'e',
         15:'f',
         16:'g',
         17:'h',
         18:'i',
         19:'j',
         20:'k',
         21:'l',
         22:'m',
         23:'n',
         24:'o',
         25:'p',
         26:'q',
         27:'r',
         28:'s',
         29:'t',
         30:'u',
         31:'v',
         32:'w',
         33:'x',
         34:'y',
         35:'z'}
    new_num_string=''
    current=num
    while current!=0:
        remainder=current%n
        if 36>remainder>9:
            remainder_string=num_rep[remainder]
        elif remainder>=36:
            remainder_string='('+str(remainder)+')'
        else:
            remainder_string=str(remainder)
        new_num_string=remainder_string+new_num_string
        current=current/n
    return new_num_string

Discussion

The main use for this function is to be able to easily know what a number looks like in binary, octal, and hexidecimal; binary especially. It is very useful for when you are dealing with bitwise operations and you want to know what the actual bits for a number look like.

As suggested by Hamish Lawson, the num_rep dictionary could be done using a for loop if you prefer:

starting_num=10 num_rep={} for letter in string.lowercase: num_rep[starting_num]=letter starting_num+=1

I have chosen to leave it as-is since the above has a very slight performance hit from the for loop and the way I have it in the code is more verbose.

Comments

  1. 1. At 12:47 a.m. on 15 jun 2001, Klaus Alexander Seistrup said:

    Max is base-36. Just as 9 is the biggest number in base-10, z is the biggest number in base-36, not base-35.

  2. 2. At 1:17 a.m. on 15 jun 2001, Hamish Lawson said:

    Setting up num_rep more concisely? Couldn't the num_rep dictionary be more concisely set up using a for loop (assuming you want to stick to doing the conversion using dictionary lookup rather than computation)?

  3. 3. At 3:11 p.m. on 20 jan 2003, Martin v. Löwis said:

    Input is not decimal. The description of the algorithm is incorrect: it does not take a decimal number as input, but a Python integer object - whether this was created through a decimal literal, or through a hexadecimal one, or through some other expression is irrelevant.

  4. 4. At 3:41 p.m. on 30 jun 2004, Greg Jorgensen said:

    quite a few problems, and not a great implementation. This code fails in several ways:

    • base = 1 causes infinite loop

    • base = 0 causes divide by zero

    • non-numerics passed for number or base cause exception

    • incorrectly converts number 0 to "" for any base

    The code is much longer than it should be. Using a dictionary for a sequentially-indexed list of letters is wasteful; even worse is the suggestion to populate the dictionary with a for loop. Making the distinction between digits in the 0..9 range and digits in the a..z range over-complicates the code. The digits needed for bases 2..36 are a constant string.

    Here's a version that doesn't have these problems (though it may have other bugs). And I think it is more Pythonic.

    def baseconvert(n, base):
        """convert positive decimal integer n to equivalent in another base (2-36)"""
    
        digits = "0123456789abcdefghijklmnopqrstuvwxyz"
    
        try:
            n = int(n)
            base = int(base)
        except:
            return ""
    
        if n < 0 or base < 2 or base > 36:
            return ""
    
        s = ""
        while 1:
            r = n % base
            s = digits[r] + s
            n = n / base
            if n == 0:
                break
    
        return s
    

    It doesn't deal with base > 36 but it should be obvious how to extend the code to do it.

    Greg Jorgensen

    PDXperts LLC - Portland, Oregon USA

  5. 5. At 9:12 a.m. on 28 sep 2007, Aloysio Figueiredo said:
    def base10toN(num,n):
       return ((num == 0) and  "0" ) or ( base10toN(num // n, n).strip("0") + "0123456789abcdefghijklmnopqrstuvwxyz"[:n][num % n])
    

    :)

  6. 6. At 9:14 a.m. on 28 sep 2007, Aloysio Figueiredo said:

    Or to put it really short:

    def base10toN(num,n):
       return ((num == 0) and  "0" ) or ( base10toN(num // n, n).strip("0") + "0123456789abcdefghijklmnopqrstuvwxyz"[:n][num % n])
    

    :)

  7. 7. At 6:31 p.m. on 6 mar 2008, Kip Bryan said:

    Aloysio's short, but fixed. Thanks Aloysio for the inspiring one-liner version. However it didn't work for numbers that end in zero, such as 4 in base 2, which should be 100 but it gives 10. Here's an adjusted one. Note that none of these are converting from "base 10" as integers are stored in binary, normally. This also omits the [:n] substring from the original.

    def baseN(num,b):
      return ((num == 0) and  "0" ) or ( baseN(num // b, b).lstrip("0") + "0123456789abcdefghijklmnopqrstuvwxyz"[num % b])
    
  8. 8. At 6:45 p.m. on 1 feb 2009, Anonymous said:

    Made more generic and pythonic still:

    def baseN(num,b,numerals="0123456789abcdefghijklmnopqrstuvwxyz"):
        return ((num == 0) and  "0" ) or ( baseN(num // b, b).lstrip("0") + numerals[num % b])
    
  9. 9. At 1:50 a.m. on 17 aug 2009, wanyewon said:

    The previous versions had a few bugs: They didn't pass the numerals to the recursion call so you could only use different numerals for small numbers. Crazy cases like negative numbers and base 1 caused infinite recursions. The readability was that of an ancient Egyptian scroll.

    Here's my take on this:

    def baseN(num, base, numerals="0123456789abcdefghijklmnopqrstuvwxyz"):
        """
        Convert any int to base/radix 2-36 string. Special numerals can be used
        to convert to any base or radix you need. This function is essentially
        an inverse int(s, base).
    
        For example:
        >>> baseN(-13, 4)
        '-31'
        >>> baseN(91321, 2)
        '10110010010111001'
        >>> baseN(791321, 36)
        'gyl5'
        >>> baseN(91321, 2, 'ab')
        'babbaabaababbbaab'
        """
        if num == 0:
            return "0"
    
        if num < 0:
            return '-' + baseN((-1) * num, base, numerals)
    
        if not 2 <= base <= len(numerals):
            raise ValueError('Base must be between 2-%d' % len(numerals))
    
        left_digits = num // base
        if left_digits == 0:
            return numerals[num % base]
        else:
            return baseN(left_digits, base, numerals) + numerals[num % base]
    

Sign in to comment