ActiveState Code

Recipe 222109: radix.str(N,R) -- reverse function to int(S,R) and long(S,R)


The function produces an arbitrary radix string representation of integer numbers.

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# radix.py

"""
Defines str(number,radix) -- reverse function to int(str,radix) and long(str,radix)

number -- signed int or long,
radix  -- 2 to 36

Usage:
import radix
str_repr = radix.str( number, radix )

print radix.str( 10, 16 ), radix.str( 1570137287, 36 ) # a python

"""


import string


def str( number, radix ):
   """str( number, radix ) -- reverse function to int(str,radix) and long(str,radix)"""

   if not 2 <= radix <= 36:
      raise ValueError, "radix must be in 2..36"

   abc = string.digits + string.letters

   result = ''

   if number < 0:
      number = -number
      sign = '-'
   else:
      sign = ''

   while True:
      number, rdigit = divmod( number, radix )
      result = abc[rdigit] + result
      if number == 0:
         return sign + result

   # never here because number >= 0, radix > 0, we repeat (number /= radix)


if __name__ == '__main__':
   src = 'qwertyuioplkjhgfdsazxcvbnm0987654321'
   dst = 79495849566202193863718934176854772085778985434624775545L

   num = int( src, 36 )
   assert num == dst
   res = str( num, 36 )
   assert res == src
   print "%s radix 36 is\n%d decimal" % (src, dst)


# EOF

Discussion

I've discovered that a function returning an arbitrary radix string from a number is missing in the language. So I implemented it to restore the symmetry.

Moon aka Sun

Comments

  1. 1. At 12:09 a.m. on 17 sep 2003, Moon aka Sun (the author) said:

    --> Category: Algorithms. I'm sorry, it belongs rather to the Algorithms Category.

    See also here: Numeric base converter that accepts arbitrary digits

    http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/111286

  2. 2. At 12:10 a.m. on 17 sep 2003, Raymond Hettinger said:

    A few nits. Nicely written.

    Here are a few nits to take it to industrial strength:

    • rename the function to base() or some such to avoid overwriting __builtin__.str()

    • only build abc once and do it outside the function

    • use ''.join() on a list instead of quadratic time string addition.

    • make divmod, abc, and result.append into locals for faster lookup in the loop.

      import string abc = string.digits + string.letters

      def base(number, radix): """base(number, radix) inverse function to int(str,radix) and long(str,radix) """

      if not 2 <= radix <= 36: raise ValueError, "radix must be in 2..36"

      result = [] addon = result.append if number < 0: number = -number addon('-') elif number == 0: addon('0')

      _divmod, _abc = divmod, abc while number: number, rdigit = _divmod(number, radix) addon(_abc[rdigit])

      result.reverse() return ''.join(result)

  3. 3. At 11:16 a.m. on 15 oct 2007, Richard Nichols III said:
    added an optional minimim-width parameter.
    note: '0' * -1 == ''
    
    import string
    abc = string.digits + string.letters
    
    def base(number, radix, width=1):
       """base(number, radix)
             inverse function to int(str,radix) and long(str,radix)
       """
    
       if not 2 &lt;= radix &lt;= 36:
          raise ValueError, "radix must be in 2..36"
    
       result = []
       addon = result.append
       if number &lt;
          number = -number
          addon('-')
    
       _divmod, _abc = divmod, abc
       while number:
          number, rdigit = _divmod(number, radix)
          addon(_abc[rdigit])
    
       addon("0"*(width-len(result)))
    
       result.reverse()
       return ''.join(result)
    
  4. 4. At 11:53 p.m. on 17 jun 2008, Damon McCormick said:

    Minus sign should be at the beginning. The code directly above puts the minus sign at the end instead of the beginning.

    Here's the version I ended up with, which also allows you to use whatever characters you like as the digits:

    def toRadixStr (number, radix, width=1,
              digits=string.digits+string.letters):
       """Inverse function to int(str,radix) and long(str,radix)."""
       digitCount = len(digits)
       if not 2 &lt;= radix &lt;= digitCount:
          raise ValueError, "radix must be in 2..%d" % digitCount
       result = []
       # convert some globals to locals for speed
       _divmod, _digits = divmod, digits
       append = result.append
       # check for a negative number
       if number &lt; 0:
           number = -number
           isNegative = True
       else:
           isNegative = False
       # append digits in reverse order
       while number:
          (number, remainder) = _divmod(number, radix)
          append(_digits[remainder])
       # append leading 0s
       leading0s = digits[0] * (width-len(result)) # note: "0" * -1 == ''
       append(leading0s)
       # append sign if necessary and reverse
       if isNegative:
          append('-')
       result.reverse()
       return ''.join(result)
    
  5. 5. At 12:38 a.m. on 18 jun 2008, Damon McCormick said:

    typo. string.letters should be string.uppercase (or string.lowercase).

Sign in to comment