# Python elegant inverse function of int(string,base)

Posted on

### Question :

Python elegant inverse function of int(string,base)

python allows conversions from string to integer using any base in the range [2,36] using:

``````int(string,base)
``````

im looking for an elegant inverse function that takes an integer and a base and returns a string

for example

``````>>> str_base(224,15)
'ee'
``````

i have the following solution:

``````def digit_to_char(digit):
if digit < 10: return chr(ord('0') + digit)
else: return chr(ord('a') + digit - 10)

def str_base(number,base):
if number < 0:
return '-' + str_base(-number,base)
else:
(d,m) = divmod(number,base)
if d:
return str_base(d,base) + digit_to_char(m)
else:
return digit_to_char(m)
``````

note: digit_to_char() works for bases <= 169 arbitrarily using ascii characters after ‘z’ as digits for bases above 36

is there a python builtin, library function, or a more elegant inverse function of int(string,base) ?

This thread has some example implementations.

Actually I think your solution looks rather nice, it’s even recursive which is somehow pleasing here.

I’d still simplify it to remove the `else`, but that’s probably a personal style thing. I think `if foo: return` is very clear, and doesn’t need an `else` after it to make it clear it’s a separate branch.

``````def digit_to_char(digit):
if digit < 10:
return str(digit)
return chr(ord('a') + digit - 10)

def str_base(number,base):
if number < 0:
return '-' + str_base(-number, base)
(d, m) = divmod(number, base)
if d > 0:
return str_base(d, base) + digit_to_char(m)
return digit_to_char(m)
``````

I simplified the 0-9 case in `digit_to_char()`, I think `str()` is clearer than the `chr(ord())` construct. To maximize the symmetry with the `>= 10` case an `ord()` could be factored out, but I didn’t bother since it would add a line and brevity felt better. 🙂

Maybe this shouldn’t be an answer, but it could be helpful for some: the built-in `format` function does convert numbers to string in a few bases:

``````>>> format(255, 'b') # base 2
'11111111'
>>> format(255, 'd') # base 10
'255'
>>> format(255, 'o') # base 8
'377'
>>> format(255, 'x') # base 16
'ff'
``````

If you use Numpy, there is `numpy.base_repr`.

You can read the code under `numpy/core/numeric.py`. Short and elegant

The above answers are really nice. It helped me a lot to prototype an algortithm I had to implement in C

I’d like to come up with a little change (I used) to convert decimal to a base of symbolspace

I also ignored negativ values just for shortness and the fact that’s mathematical incorrect
–> other rules for modular arithmetics
–> other math if you use binary, oct or hex –> diff in unsigned & signed values

``````def str_base(number, base):
(d,m) = divmod(number,len(base))
if d > 0:
return str_base(d,base)+base[m]
return base[m]
``````

that lead’s to following output

``````>>> str_base(13,'01')
'1101'
>>> str_base(255,'01')
'11111111'
>>> str_base(255,'01234567')
'377'
>>> str_base(255,'0123456789')
'255'
>>> str_base(255,'0123456789abcdef')
'ff'
>>> str_base(1399871903,'_helowrd')
'hello_world'
``````

if you want to padd with the propper zero symbol you can use

``````symbol_space = 'abcdest'

>>> str_base(734,symbol_space).rjust(0,symbol_space)
'catt'
>>> str_base(734,symbol_space).rjust(6,symbol_space)
'aacatt'
``````

review this.

``````def int2str(num, base=16, sbl=None):
if not sbl:
sbl = '0123456789abcdefghijklmnopqrstuvwxyz'
if len(sbl) < 2:
raise ValueError, 'size of symbols should be >= 2'
if base < 2 or base > len(sbl):
raise ValueError, 'base must be in range 2-%d' % (len(sbl))

neg = False
if num < 0:
neg = True
num = -num

num, rem = divmod(num, base)
ret = ''
while num:
ret = sbl[rem] + ret
num, rem = divmod(num, base)
ret = ('-' if neg else '') + sbl[rem] + ret

return ret
``````

`digit_to_char` could be implemented like this:

``````def digit_to_char(digit):
return (string.digits + string.lowercase)[digit]
``````

I had once written my own function with the same goal but is now embarrassingly complicated.

``````from math import log, ceil, floor
from collections import deque
from itertools import repeat
from string import uppercase, digits
import re

__alphanumerals = (digits + uppercase)

class InvalidBaseError(ValueError): pass
class FloatConvertError(ValueError): pass
class IncorrectBaseError(ValueError): pass

def getbase(number, base=2, frombase = 10):
if not frombase == 10:
number = getvalue(number, frombase)
#getvalue is also a personal function to replicate int(number, base)

if 1 >= base or base >= len(__alphanumerals) or not floor(base) == base:
raise InvalidBaseError("Invalid value: {} entered as base to convert
to. n{}".format(base,
"Assert that the base to convert to is a decimal integer."))

if isinstance(number, str):
try:
number = atof(number)
except ValueError:
#The first check of whether the base is 10 would have already corrected the number
raise IncorrectBaseError("Incorrect base passed as base of number -> number: {} base: {}".format(number, frombase))
#^ v was supporting float numbers incase number was the return of another operation
if number > floor(number):
raise FloatConvertError("The number to be converted must not be a float. {}".format(number))

isNegative = False
if number < 0:
isNegative = True
number = abs(number)

logarithm = log(number, base) if number else 0 #get around number being zero easily

ceiling = int(logarithm) + 1

structure = deque(repeat(0, ceiling), maxlen = ceiling)

while number:
if number >= (base ** int(logarithm)):
acceptable_digit = int(number / (base ** floor(logarithm)))
structure.append(acceptable_digit if acceptable_digit < 10 else     __alphanumerals[acceptable_digit])
number -= acceptable_digit * (base ** floor(logarithm))
else:
structure.append(0)

logarithm -= 1

while structure == 0:
#the result needs trailing zeros
structure.rotate(-1)

return ("-" if isNegative and number else "") + reduce(lambda a, b: a + b, map(lambda a: str(a), structure))
``````

I think though that the function strbase should only support bases >= 2 and <= 36 to prevent conflict with other tools in python such as int.
Also, I think that only one case of alphabets should be used preferably uppercase again to prevent conflict with other functions like int since it will consider both “a” and “A” to be 10.

``````from string import uppercase

dig_to_chr = lambda num: str(num) if num < 10 else uppercase[num - 10]

def strbase(number, base):
if not 2 <= base <= 36:
raise ValueError("Base to convert to must be >= 2 and <= 36")

if number < 0:
return "-" + strbase(-number, base)

d, m = divmod(number, base)
if d:
return strbase(d, base) + dig_to_chr(m)

return dig_to_chr(m)
``````

Here’s my solution:

``````def int2base(a, base, numerals="0123456789abcdefghijklmnopqrstuvwxyz"):
baseit = lambda a=a, b=base: (not a) and numerals or baseit(a-a%b,b*base)+numerals[a%b%(base-1) or (a%b) and (base-1)]
return baseit()
``````

## Explanation

In any base, every number is equal to `a1+a2*base**2+a3*base**3...`. The “mission” is to find all a’s.

For every `N=1,2,3...`, the code is isolating the `aN*base**N` by “mouduling” by b for `b=base**(N+1)` which slice all a’s bigger than N, and slicing all the a’s that their serial is smaller than N by decreasing a every time the function is called by the current `aN*base**N`.

`Base%(base-1)==1` therefore `base**p%(base-1)==1` and therefore `q*base^p%(base-1)==q` with only one exception when `q=base-1` which returns `0`.
To fix that, in case it returns `0`, the function is checking is it `0` from the beginning.