Home » Python » How to convert an integer in any base to a string?

How to convert an integer in any base to a string?

Questions:

Python allows easy creation of an integer from a string of a given base via

int(str, base).

I want to perform the inverse: creation of a string from an integer,
i.e. I want some function int2base(num, base), such that:

int(int2base(x, b), b) == x

The function name/argument order is unimportant.

For any number x and base b that int() will accept.

This is an easy function to write: in fact it’s easier than describing it in this question. However, I feel like I must be missing something.

I know about the functions bin, oct, hex, but I cannot use them for a few reasons:

• Those functions are not available on older versions of Python, with which I need compatibility with (2.2)

• I want a general solution that can be called the same way for different bases

• I want to allow bases other than 2, 8, 16

Related

If you need compatibility with ancient versions of Python, you can either use gmpy (which does include a fast, completely general int-to-string conversion function, and can be built for such ancient versions — you may need to try older releases since the recent ones have not been tested for venerable Python and GMP releases, only somewhat recent ones), or, for less speed but more convenience, use Python code — e.g., most simply:

import string
digs = string.digits + string.letters

def int2base(x, base):
if x < 0:
sign = -1
elif x == 0:
return digs
else:
sign = 1

x *= sign
digits = []

while x:
digits.append(digs[x % base])
x /= base

if sign < 0:
digits.append('-')

digits.reverse()

return ''.join(digits)

Questions:
def baseN(num,b,numerals="0123456789abcdefghijklmnopqrstuvwxyz"):
return ((num == 0) and numerals) or (baseN(num // b, b, numerals).lstrip(numerals) + numerals[num % b])

RuntimeError: maximum recursion depth exceeded in cmp

for very big integers.

Questions:
"{0:b}".format(100) # bin: 1100100
"{0:x}".format(100) # hex: 64
"{0:o}".format(100) # oct: 144

Questions:

Surprisingly, people were giving only solutions that convert to small bases (smaller then the length of the English alphabet). There was no attempt to give a solution which converts to any arbitrary base from 2 to infinity.

So here is a super simple solution:

def numberToBase(n, b):
if n == 0:
return 
digits = []
while n:
digits.append(int(n % b))
n /= b
return digits[::-1]

so if you need to convert some super huge number to the base 577,

numberToBase(67854 ** 15 - 102, 577), will give you a correct solution:
[4, 473, 131, 96, 431, 285, 524, 486, 28, 23, 16, 82, 292, 538, 149, 25, 41, 483, 100, 517, 131, 28, 0, 435, 197, 264, 455],

Which you can later convert to any base you want

Questions:

I guess the answer to my question was “no” I was not missing some obvious solution.
Here is the function I will use that condenses the good ideas expressed in the answers.

• allow caller-supplied mapping of characters (allows base64 encode)
• checks for negative and zero
• maps complex numbers into tuples of strings

def int2base(x,b,alphabet='0123456789abcdefghijklmnopqrstuvwxyz'):
'convert an integer to its string representation in a given base'
if b<2 or b>len(alphabet):
if b==64: # assume base64 rather than raise error
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
else:
raise AssertionError("int2base base out of range")
if isinstance(x,complex): # return a tuple
return ( int2base(x.real,b,alphabet) , int2base(x.imag,b,alphabet) )
if x<=0:
if x==0:
return alphabet
else:
return  '-' + int2base(-x,b,alphabet)
# else x is non-negative real
rets=''
while x>0:
x,idx = divmod(x,b)
rets = alphabet[idx] + rets
return rets

Questions:

Python doesn't have a built-in function for printing an integer in an arbitrary base. You'll have to write your own if you want to.

Questions:

You could use baseconv.py: https://github.com/semente/python-baseconv

Sample usage:

>>> from baseconv import BaseConverter
>>> base20 = BaseConverter('0123456789abcdefghij')
>>> base20.encode(1234)
'31e'
>>> base20.decode('31e')
'1234'
>>> base20.encode(-1234)
'-31e'
>>> base20.decode('-31e')
'-1234'
>>> base11 = BaseConverter('0123456789-', sign='\$')
>>> base11.encode('\$1234')
'\$-22'
>>> base11.decode('\$-22')
'\$1234'

There is some bultin converters as for example baseconv.base2, baseconv.base16 and baseconv.base64.

Questions:

http://code.activestate.com/recipes/65212/

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

Here's another one from the same link

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

Questions:

I made a pip package for this.

I recommend you use my bases.py https://github.com/kamijoutouma/bases.py which was inspired by bases.js

from bases import Bases
bases = Bases()

bases.toBase16(200)                // => 'c8'
bases.toBase(200, 16)              // => 'c8'
bases.toBase62(99999)              // => 'q0T'
bases.toBase(200, 62)              // => 'q0T'
bases.toAlphabet(300, 'aAbBcC')    // => 'Abba'

bases.fromBase16('c8')               // => 200
bases.fromBase('c8', 16)             // => 200
bases.fromBase62('q0T')              // => 99999
bases.fromBase('q0T', 62)            // => 99999
bases.fromAlphabet('Abba', 'aAbBcC') // => 300

refer to https://github.com/kamijoutouma/bases.py#known-basesalphabets
for what bases are usable

EDIT:

Questions:
def base(decimal ,base) :
list = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
other_base = ""
while decimal != 0 :
other_base = list[decimal % base] + other_base
decimal    = decimal / base
return other_base

print base(31 ,16)

output:

"1F"

Questions:
>>> import string
>>> def int2base(integer, base):
if not integer: return '0'
sign = 1 if integer > 0 else -1
alphanum = string.digits + string.ascii_lowercase
nums = alphanum[:base]
res = ''
integer *= sign
while integer:
integer, mod = divmod(integer, base)
res += nums[mod]
return ('' if sign == 1 else '-') + res[::-1]

>>> int2base(-15645, 23)
'-16d5'
>>> int2base(213, 21)
'a3'

Questions:

A recursive solution for those interested. Of course, this will not work with negative binary values. You would need to implement Two's Complement.

def generateBase36Alphabet():
return ''.join([str(i) for i in range(10)]+[chr(i+65) for i in range(26)])

def generateAlphabet(base):
return generateBase36Alphabet()[:base]

def intToStr(n, base, alphabet):
def toStr(n, base, alphabet):
return alphabet[n] if n < base else toStr(n//base,base,alphabet) + alphabet[n%base]
return ('-' if n < 0 else '') + toStr(abs(n), base, alphabet)

print('{} -> {}'.format(-31, intToStr(-31, 16, generateAlphabet(16)))) # -31 -> -1F

Questions:
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 everyN=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 everytime the func is called by the current aN*base**N .

Base%(base-1)==1 therefor base**p%(base-1)==1 and therefor 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 func is checking is it 0 from the beggining.

in this sample theres only one multiplications (instead of division) and some moudulueses which relatively takes small amounts of time.

Questions:
if not isinstance(input, int):
raise TypeError('Not an integer!')
elif power is None:
power = 1

if input == 0:
return 0
else:

if not isinstance(input, int):
raise TypeError('Not an integer!')
return sum(int(digit)*(from_radix**power) for power, digit in enumerate(str(input)[::-1]))

Questions:

Another short one (and easier to understand imo):

def int_to_str(n, b, symbols='0123456789abcdefghijklmnopqrstuvwxyz'):
return (int_to_str(n/b, b, symbols) if n >= b else "") + symbols[n%b]

And with proper exception handling:

def int_to_str(n, b, symbols='0123456789abcdefghijklmnopqrstuvwxyz'):
try:
return (int_to_str(n/b, b) if n >= b else "") + symbols[n%b]
except IndexError:
raise ValueError(
"The symbols provided are not enough to represent this number in "
"this base")

Questions:

Another solution, works with base 2 to 10, needs modification for higher bases:

def n2b(n, b):
if n == 0:
return 0
d = []
while n:
d.append(int(n % b))
n /= b
return ''.join(map(str,d[::-1]))

Example:

n2b(10,2) => '10100'
int(n2b(10,2),2) => 10

Questions:

Here is a recursive version that handles signed integers and custom digits.

import string

def base_convert(x, base, digits=None):
"""Convert integer `x` from base 10 to base `base` using `digits` characters as digits.
If `digits` is omitted, it will use decimal digits + lowercase letters + uppercase letters.
"""
digits = digits or (string.digits + string.ascii_letters)
assert 2 <= base <= len(digits), "Unsupported base: {}".format(base)
if x == 0:
return digits
sign = '-' if x < 0 else ''
x = abs(x)
first_digits = base_convert(x // base, base, digits).lstrip(digits)
return sign + first_digits + digits[x % base]

Questions:
def baseConverter(x, b):
s = ""
d = string.printable.upper()
while x > 0:
s += d[x%b]
x = x / b
return s[::-1]

Questions:

def bn(x,b,ab="0123456789abcdefghijklmnopqrstuvwxyz..."
a = ""
while (x>0):
x,r = divmod(x,n)
a += ab[r] return a[::-1]

bn(2**100, 36)
output:

3ewfdnca0n6ld1ggvfgg

to convert to any base, inverse is easy too.

Questions:

Strings aren't the only choice for representing numbers: you can use a list of integers to represent the order of each digit. Those can easily be converted to a string.

None of the answers reject base < 2; and most will run very slowly or crash with stack overflows for very large numbers (such as 56789 ** 43210). To avoid such failures, reduce quickly like this:

def n_to_base(n, b):
if b < 2: raise # invalid base
if abs(n) < b: return [n]
ret = [y for d in n_to_base(n, b*b) for y in divmod(d, b)]
return ret[1:] if ret == 0 else ret # remove leading zeros

def base_to_n(v, b):
h = len(v) // 2
if h == 0: return v
return base_to_n(v[:-h], b) * (b**h) + base_to_n(v[-h:], b)

assert ''.join(['0123456789'[x] for x in n_to_base(56789**43210,10)])==str(56789**43210)

Speedwise, n_to_base is comparable with str for large numbers (about 0.3s on my machine), but if you compare against hex you may be surprised (about 0.3ms on my machine, or 1000x faster). The reason is because the large integer is stored in memory in base 256 (bytes). Each byte can simply be converted to a two-character hex string. This alignment only happens for bases that are powers of two, which is why there are special cases for 2,8, and 16 (and base64, ascii, utf16, utf32).

Consider the last digit of a decimal string. How does it relate to the sequence of bytes that forms its integer? Let's label the bytes s[i] with s being the least significant (little endian). Then the last digit is sum([s[i]*(256**i) % 10 for i in range(n)]). Well, it happens that 256**i ends with a 6 for i > 0 (6*6=36) so that last digit is (s*5 + sum(s)*6)%10. From this, you can see that the last digit depends on the sum of all the bytes. This nonlocal property is what makes converting to decimal harder.

Questions:

I have not seen any converters of float here. And I missed the grouping for always three digits.

TODO:

-numbers in scientific expression (n.nnnnnn*10**(exp) -- the '10' is self.baseDigits[1::-1]/self.to_string(len (self.baseDigits))

-from_string-function.

-base 1 -> roman numbers?

-repr of complex with agles

So here is my solution:

DIGITS = "0123456789abcdefghijklmnopqrstuvwxyz"

# note that the order of the digits is reversed for digits before the point
NO_GROUPING = lambda g: g

concat = "".join
concat_backwards = lambda g: concat(e for e in reversed(list(g)))

def grouping(length = 3, char = '_'):
def yieldor(digits):
i = 0
for d in digits:
if i == length:
yield char
i = 0
yield d
i+=1

return yieldor

class Converter:
def __init__(self, baseDigits: (int, str), beforePoint = NO_GROUPING, afterPoint = NO_GROUPING, decimalPoint = '.', digitPrecision = 16, trimZeros = True):
if isinstance(baseDigits, int):
baseDigits = DIGITS[:baseDigits]
self.baseDigits = baseDigits

self.beforePoint = beforePoint
self.afterPoint  = afterPoint

self.decimalPoint = decimalPoint
self.digitPrecision = digitPrecision
self.trimZeros = trimZeros

def to_string(self, number: (int, float, complex)) -> str:
if isinstance(number, complex):
if number.imag == 0:
return self.to_string(number.real)
if number.real == 0:
return self.to_string(number.imag) + 'j'
return "({}+{}j)".format(self.to_string(number.real), self.to_string (number.imag))
if number < 0:
return '-' + self.to_string(-number)
digitCount = len(self.baseDigits)
if isinstance(number, float):
# round correctly
precError=digitCount**-self.digitPrecision
number+=0.5*precError
if self.trimZeros:
def yieldor(n):
p = precError
for i in range(self.digitPrecision):
if n <= p:
return
p *= digitCount
n *= digitCount
digit = int(n)
n -= digit
yield self.baseDigits[digit]
else:
def yieldor(n):
for i in range(self.digitPrecision):
n *= digitCount
digit = int(n)
n -= digit
yield self.baseDigits[digit]

a = concat(self.afterPoint(yieldor(number%1)))

return (
self.to_string(int(number)) + (a and self.decimalPoint + a)
)

else: #is int
if not number: return self.baseDigits
def yieldor(n):
while n:
n, digit = divmod(n, digitCount)
yield self.baseDigits[digit]
return concat_backwards(self.beforePoint(yieldor(number)))

# some tests:
if __name__ == "__main__":
def conv_test(num, digits, *argv, **kwv):
print(num, "->", digits if isinstance(digits, int) else "{} ({})".format(len(digits), digits), Converter(digits, *argv, **kwv).to_string(num))
conv_test(True, "ft")
conv_test(123, 12, grouping(2))
conv_test(-0xf00d, 16)
conv_test(1000, True<<True, grouping(4))
conv_test(1_000_000, "0+-", beforePoint = grouping(2, '|'))
conv_test(1.5, 10)
conv_test(0.999999999, 10, digitPrecision = 8)
conv_test(-0.1, 10)

import math
conv_test(math.pi, 10, afterPoint = grouping(5, ' '))
conv_test(0.123456789, 10, digitPrecision = 6)

grSpc = grouping(1, ' ')
conv_test(math.e, ["off", "on"], grSpc, grSpc, " dot ", digitPrecision = 7)

conv_test(1 + 1.5j, 10)

conv_test(50j, 10)

conv_test(10.01, '-<>')

# and generate some brainfuck-code here:
conv_test(1701**42, '+-<>,.][', digitPrecision = 32)