How to convert an integer to a string in any base?
Asked Answered
O

36

331

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

Osier answered 15/2, 2010 at 16:33 Comment(3)
Surprisingly no one gave a solution which works with arbitrary big base (1023). If you need it, check my solution which works for every base (2 to inf) https://mcmap.net/q/98409/-how-to-convert-an-integer-to-a-string-in-any-baseAstri
I have incorporated the solution with arbitrary big bases into my code as standard for bases larger 62 and provided also reverse conversion in the same function. So if there is anyone interested, just check it out: https://mcmap.net/q/98409/-how-to-convert-an-integer-to-a-string-in-any-base .Lidalidah
Hi, there exists a library called Basencode for this problem, please check out my answer. Hope it helps!Yucca
P
127

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., for Python 2, most simply:

import string
digs = string.digits + string.ascii_letters


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

    x *= sign
    digits = []

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

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

    digits.reverse()

    return ''.join(digits)

For Python 3, int(x / base) leads to incorrect results, and must be changed to x // base:

import string
digs = string.digits + string.ascii_letters


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

    x *= sign
    digits = []

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

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

    digits.reverse()

    return ''.join(digits)
Principalities answered 15/2, 2010 at 16:44 Comment(17)
Just in (gmpy2) case the func Alex speaks of seems to be gmpy2.digits(x, base).Leucas
It was brought to my attention that some cases need a base > 36 and so digs should be digs = string.digits + string.lowercase + string.uppercaseCarr
(or string.digits + string.letters)Cannon
Any idea why the convert-base-N-to-string isn't included by default in Python? (It is in Javascript.) Yeah, we can all write our own implementation, but I've been searching around on this site and elsewhere, and many of them have bugs. Better to have one tested, reputable version included in the core distribution.Rudolf
Found a small bug in this excellent routine for the zero case when using a number system that does not start with '0'. Only a one line change, instead of elif x==0: return '0' =====> needs to be elif x==0: return digs[0]Carr
Might be good to add a check that 2 <= base <= 62. Otherwise, if e.g. 1 is passed in for base, it will go into an infinite loop.Throughput
Should the reverse be there? >>> int2base(26, 26) 'BA' With reverse taken out: >>> int2base(26, 26) 'AB'Schmuck
In Python 2, use string.ascii_letters instead of string.letters. Works in at least Python 2.7 too; haven't tested 2.6.Protecting
string.letters won't work on Python 3, you need to use string.ascii_letters. See here in this questionStoner
I have found that in Python 3 (tested with 3.4.3 on Windows) the x /= base will cause problems because the integer will become a decimal number. I fixed it with x = int(x / base)Hostelry
Add if base == 1: return ''.join(['1' for _ in range(x)]) to the start of the function for unary (base-1) support ;)Styx
@Hostelry You can also use x //= base which behaves like /= in Python 2 in dropping the decimal. This answer should include a disclaimer that it's for Python 2.Fellers
Sooner or later someone will get an obscenity as a result of this, as I just realised (in testing, fortunately, as my users would not smile and move on). Solution - have a different set of digs that excludes vowelsSeline
int(x / base) still leads to incorrect results. The correct fix is x // base.Convalescent
@Convalescent what special cases would cause that issue?Vivacity
@Vivacity A great example is 9223372036854776832, or ((1<<53)+1) << 10. Using x // base gives the correct result, 1000000000000000000000000000000000000000000000000000010000000000, while using the code in the sample will give 1000000000000000000000000000000000000000000000000000000000000000.Reluctivity
@AlexanderHuszagh ah, so the issue is imprecision of floating point arithmetic.Vivacity
A
233

Surprisingly, people were giving only solutions that convert to small bases (smaller than 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 [0]
    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

  1. at some point of time you will notice that sometimes there is no built-in library function to do things that you want, so you need to write your own. If you disagree, post you own solution with a built-in function which can convert a base 10 number to base 577.
  2. this is due to lack of understanding what a number in some base means.
  3. I encourage you to think for a little bit why base in your method works only for n <= 36. Once you are done, it will be obvious why my function returns a list and has the signature it has.
Astri answered 23/2, 2015 at 2:48 Comment(9)
Excellent answer! Good thinking leaving the number in a list; it makes it easier to come up with one's own character representation of numbers in different bases.Intemerate
How would you then convert that list into a single character representation, if you had for example a string of characters like 0-9 plus A-Z plus 541 extra unicode characters? s = "0123456789ABCDEF" n = [15,1,13] "".join([s[x] for x in n])Telescopic
@Telescopic Sure, that way works. Of course, you have to decide which symbols to use and in what order.Difference
You can extend your answer to include base 1. if b == 1: return n * [1]Jankey
Hi, a reply to point 1 - we do have a library for this, please check out my answer. +1 for your valuable answer - my answer just adds to yours!Yucca
How would you convert that list back into a single number? Knowing it's the same base? ex: 577 in this caseRyals
This is an interesting answer. On the one hand, it gets nicely at the abstract concept that an arbitrary base is just a positional numbering system, so returning an array of numbers for each place is sort of reasonable. On the other hand, when speaking of a "base", I think most people (and wikipedia's entry on Radix would imply) that one is speaking specifically about the digits used to represent a number in such system. Converting to digits is fundamentally a part of the problem -- we want a string of characters, not an array of numbers. For bases up to 36, there are standard choices.Nash
This is wrong. Doesn't achieve the requested int(int2base(x, b), b) == x. Not even if you used the requested name.Accelerator
Why int(n % b) instead of just n % b?Accelerator
P
127

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., for Python 2, most simply:

import string
digs = string.digits + string.ascii_letters


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

    x *= sign
    digits = []

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

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

    digits.reverse()

    return ''.join(digits)

For Python 3, int(x / base) leads to incorrect results, and must be changed to x // base:

import string
digs = string.digits + string.ascii_letters


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

    x *= sign
    digits = []

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

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

    digits.reverse()

    return ''.join(digits)
Principalities answered 15/2, 2010 at 16:44 Comment(17)
Just in (gmpy2) case the func Alex speaks of seems to be gmpy2.digits(x, base).Leucas
It was brought to my attention that some cases need a base > 36 and so digs should be digs = string.digits + string.lowercase + string.uppercaseCarr
(or string.digits + string.letters)Cannon
Any idea why the convert-base-N-to-string isn't included by default in Python? (It is in Javascript.) Yeah, we can all write our own implementation, but I've been searching around on this site and elsewhere, and many of them have bugs. Better to have one tested, reputable version included in the core distribution.Rudolf
Found a small bug in this excellent routine for the zero case when using a number system that does not start with '0'. Only a one line change, instead of elif x==0: return '0' =====> needs to be elif x==0: return digs[0]Carr
Might be good to add a check that 2 <= base <= 62. Otherwise, if e.g. 1 is passed in for base, it will go into an infinite loop.Throughput
Should the reverse be there? >>> int2base(26, 26) 'BA' With reverse taken out: >>> int2base(26, 26) 'AB'Schmuck
In Python 2, use string.ascii_letters instead of string.letters. Works in at least Python 2.7 too; haven't tested 2.6.Protecting
string.letters won't work on Python 3, you need to use string.ascii_letters. See here in this questionStoner
I have found that in Python 3 (tested with 3.4.3 on Windows) the x /= base will cause problems because the integer will become a decimal number. I fixed it with x = int(x / base)Hostelry
Add if base == 1: return ''.join(['1' for _ in range(x)]) to the start of the function for unary (base-1) support ;)Styx
@Hostelry You can also use x //= base which behaves like /= in Python 2 in dropping the decimal. This answer should include a disclaimer that it's for Python 2.Fellers
Sooner or later someone will get an obscenity as a result of this, as I just realised (in testing, fortunately, as my users would not smile and move on). Solution - have a different set of digs that excludes vowelsSeline
int(x / base) still leads to incorrect results. The correct fix is x // base.Convalescent
@Convalescent what special cases would cause that issue?Vivacity
@Vivacity A great example is 9223372036854776832, or ((1<<53)+1) << 10. Using x // base gives the correct result, 1000000000000000000000000000000000000000000000000000010000000000, while using the code in the sample will give 1000000000000000000000000000000000000000000000000000000000000000.Reluctivity
@AlexanderHuszagh ah, so the issue is imprecision of floating point arithmetic.Vivacity
B
124
"{0:b}".format(100) # bin: 1100100
"{0:x}".format(100) # hex: 64
"{0:o}".format(100) # oct: 144
Bowel answered 5/9, 2010 at 19:23 Comment(7)
But it only does those three bases?Lamoreaux
Yes, unfortunately you can't specify custom int base. More info is here: docs.python.org/library/string.html#formatstringsBowel
The 0 is unnecessary. Here's the Python 2 documentation: docs.python.org/2/library/string.html#format-string-syntaxHerat
You can achieve the same results with hex(100)[2:], oct(100)[2:] and bin(100)[2:].Pages
@EvgeniSergeev: It's only unnecessary on 2.7/3.1+. On 2.6, the explicit position (or name) is required.Griqua
Just a minor correction to @ Sassan's comment above - for oct, it should be oct(100)[1:] not oct(100)[2:]. (oct numbers are represented by a single leading 0).Jarv
I think you're mistaken @Hari: The Python docs say oct does the following: Convert an integer number to an octal string prefixed with “0o”., and I also was able to confirm that on my own interpreter.Shifra
A
104
def baseN(num,b,numerals="0123456789abcdefghijklmnopqrstuvwxyz"):
    return ((num == 0) and numerals[0]) or (baseN(num // b, b, numerals).lstrip(numerals[0]) + numerals[num % b])

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

Please be aware that this may lead to

RuntimeError: maximum recursion depth exceeded in cmp

for very big integers.

Aiglet answered 15/2, 2010 at 16:41 Comment(11)
Elegant in its brevity. It seems to work under python 2.2.3 for non-negative integers. A negative number infinitely recurses.Osier
+1 useful; fixed a problem when numerals didn't start with '0'Rubeola
This fails silently (a) when base is > len(numerals), and (b) num % b is, by luck, < len(numerals). e.g. although the numerals string is only 36 characters in length, baseN(60, 40) returns '1k' while baseN(79, 40) raises an IndexError. Both should raise some kind of error. The code should be revised to raise an error if not 2 <= base <= len(numerals).Meetly
@ChrisJohnson, only if your base is not fixed in advance.Coronet
@osa, my point is the code as-written fails in a very bad way (silently, giving misleading answer) and could be fixed easily. If you are saying there would be no error if you knew in advance, for certain, that b would not exceed len(numerals), well, good luck to you.Meetly
I guess it's questionable whether the "1k" result is really wrong or not. If we give a long enough string of numerals, the result is still the same. The IndexError should clearly be handled though.Silverts
+1 for being the smallest bit of code here. The other comments are true, it doesn't properly handle some edge cases, but this is a good substitute when you have a known, limited set of bases and inputs, and limited access to third-party libraries.Polypody
My friend and I were debating a "better approximate Pi day in a different base", so I modified your code to handle floats: gist.github.com/tigerhawkvok/5c619465f9ccab0eb25ee470708bbf16 (the answer is "Base 11 Pi Day" on March 16 at 3:07:02 PM, 3.16150702865a484)Deka
The use of short-circuiting here seems needlessly confusing...why not just use an if statement...the line return numerals[0] if num == 0 else baseN(num // b, b, numerals).lstrip(numerals[0]) + numerals[num % b] is just as brief.Fierro
Alternatively, def num2str(num, chars): return chars[num] if num < len(chars) else num2str(num // len(chars)) + chars[num % len(chars)]. Then you can create functions for radixes you need. Or one function (a wrapper) for an arbitrary radix. And as you can see lstrip() can be avoided.Conflagration
Inverse to this (string to integer) in case anyone needs it: def n_base(num, b, numerals="0123456789abcdefghijklmnopqrstuvwxyz"): return 0 if len(num) == 0 else numerals.find(num[-1]) + n_base(num[:-1], b, numerals) * bConcessionaire
K
85
>>> numpy.base_repr(10, base=3)
'101'

Note that numpy.base_repr() has a limit of 36 as its base. Otherwise it throws a ValueError

Kisner answered 3/6, 2019 at 16:46 Comment(3)
Nice solution. In my case, I was avoiding numpy in clac for loading-time concerns. Preloading numpy more than triples the runtime of simple expression evaluation in clac: e.g. clac 1+1 went from about 40ms to 140ms.Osier
Which matches the limitation of the built in "int" function. Larger bases require deciding on what to do when the letters run out,Tote
I guess this is what most people who reach this question are looking for.Abreaction
D
35

Recursive

I would simplify the most voted answer to:

base_string = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
def to_base(number, base): 
    return "0" if not number else to_base(number // base, base).lstrip("0") + base_string[number % base_string]

With the same advice for RuntimeError: maximum recursion depth exceeded in cmp on very large integers and negative numbers. (You could usesys.setrecursionlimit(new_limit))

Iterative

To avoid recursion problems:

base_string = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
def to_base(number, base):
    result = ""
    while number:
        result += base_string[number % base]
        number //= base
    return result[::-1] or "0"
Defunct answered 7/12, 2018 at 19:5 Comment(5)
Beautifully refactored, and without library.Backup
Shouldn't the stop condition be return BS[0] if not n then ? Just in case you want to use fancy digits, like I do :)Pituri
@ArnaudP agreed. This one works for me: return BS[n] if n < b else to_base(n // b) + BN[n % b]Flavorous
can also use divmod in the iterative approach since one needs both the divisor and remainder, like so number, remainder = divmod(number, base)Boycie
I believe base_string[number % base_string] should be base_string[number % base]Tarrah
O
22

Great answers! 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[0]
        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

Osier answered 15/2, 2010 at 17:28 Comment(1)
How do you convert the base64 output of our function back to an integer?Egocentric
A
14

You could use baseconv.py from my project: 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.

Apophyllite answered 25/9, 2012 at 20:42 Comment(0)
U
5
def base(decimal ,base) :
    list = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    other_base = ""
    while decimal != 0 :
        other_base = list[decimal % base] + other_base
        decimal    = decimal / base
    if other_base == "":
        other_base = "0"
    return other_base

print base(31 ,16)

output:

"1F"

Ultrasonic answered 4/10, 2014 at 1:53 Comment(2)
other-base is the same as other - base, so you should use other_baseBoeotian
Also, this doesn't work correctly if decimal is zero.Boeotian
S
5
def base_conversion(num, base):
    digits = []
    while num > 0:
        num, remainder = divmod(num, base)
        digits.append(remainder)
    return digits[::-1]
Sandstone answered 4/2, 2020 at 20:46 Comment(2)
By replacing the last line with return ''.join(map(str, digits[::-1])), it's even more useful in bases between 2 and 10. It's not working for base 1.Sherrisherrie
It also doesn't work with num=0.Sherrisherrie
S
3

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
Screak answered 15/2, 2010 at 16:40 Comment(1)
base10toN does not account for the case of num == 0.Oversee
A
3

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: pip link https://pypi.python.org/pypi/bases.py/0.2.2

Academic answered 27/5, 2015 at 10:21 Comment(2)
This works like a charm for the known bases specified.Henriques
This is by far the best answer! And thanks for the pip packaging!Oakie
Y
2

While the currently top answer is definitely an awesome solution, there remains more customization users might like.

Basencode adds some of these features, including conversions of floating point numbers, modifying digits (in the linked answer, only numbers can be used).

Here's a possible use case:

>>> from basencode import *
>>> n1 = Number(12345)
>> n1.repr_in_base(64) # convert to base 64
'30V'
>>> Number('30V', 64) # construct Integer from base 64
Integer(12345)
>>> n1.repr_in_base(8)
'30071'
>>> n1.repr_in_octal() # shortcuts
'30071'
>>> n1.repr_in_bin() # equivelant to `n1.repr_in_base(2)`
'11000000111001'
>>> n1.repr_in_base(2, digits=list('-+')) # override default digits: use `-` and `+` in place of `0` and `1`
'++------+++--+'
>>> n1.repr_in_base(33) # yet another base - all bases from 2 to 64 are supported from the start
'bb3'

How would you add any bases you want? Let me replicate the example of the currently most upvoted answer: the digits parameter allows you to override the default digits from base 2 to 64, and provide digits for any base higher than that. The mode parameter determines how the value of the representation will determine how (list or string) the answer will be returned.

>>> n2 = Number(67854 ** 15 - 102)
>>> n2.repr_in_base(577, digits=[str(i) for i in range(577)], mode="l")
['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']
>>> n2.repr_in_base(577, mode="l") # the program remembers the digits for base 577 now
['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']

Operations can be done: the Number class returns an instance of basencode.Integer if the provided number is an Integer, else it returns a basencode.Float

>>> n3 = Number(54321) # the Number class returns an instance of `basencode.Integer` if the provided number is an Integer, otherwise it returns a `basencode.Float`.
>>> n1 + n3
Integer(66666)
>>> n3 - n1
Integer(41976)
>>> n1 * n3
Integer(670592745)
>>> n3 // n1
Integer(4)
>>> n3 / n1 # a basencode.Float class allows conversion of floating point numbers
Float(4.400243013365735)
>>> (n3 / n1).repr_in_base(32)
'4.cpr56v6rnc4oitoblha2r11sus0dheqd4pgechfcjklo74b2bgom7j8ih86mipdvss0068sehi9f3791mdo4uotfujq66cf0jkgo'
>>> n4 = Number(0.5) # returns a basencode.Float
>>> n4.repr_in_bin() # binary version of 0.5
'0.1'

Disclaimer: this project is under active maintenance, and I'm a contributor.

Yucca answered 5/11, 2022 at 2:12 Comment(0)
J
1
def int2base(a, base, numerals="0123456789abcdefghijklmnopqrstuvwxyz"):
    baseit = lambda a=a, b=base: (not a) and numerals[0]  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.


advantages

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

Joleen answered 24/4, 2016 at 12:7 Comment(0)
P
0
>>> 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'
Proportion answered 15/2, 2010 at 16:45 Comment(0)
T
0

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
Triable answered 27/1, 2015 at 13:5 Comment(0)
A
0

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] == 0 else ret # remove leading zeros

def base_to_n(v, b):
    h = len(v) // 2
    if h == 0: return v[0]
    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[0] 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[0]*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.

Abri answered 19/1, 2017 at 14:34 Comment(0)
H
0
def base_changer(number,base):
    buff=97+abs(base-10)
    dic={};buff2='';buff3=10
    for i in range(97,buff+1):
        dic[buff3]=chr(i)
        buff3+=1   
    while(number>=base):
        mod=int(number%base)
        number=int(number//base)
        if (mod) in dic.keys():
            buff2+=dic[mod]
            continue
        buff2+=str(mod)
    if (number) in dic.keys():
        buff2+=dic[number]
    else:
        buff2+=str(number)

    return buff2[::-1]   
Heyman answered 8/9, 2019 at 17:43 Comment(2)
In this function you can easily convert any decimal number to your favorite base.Heyman
You don't need comment your own answer, you can just edit it to add explanation.Gummite
H
0

Here is an example of how to convert a number of any base to another base.

from collections import namedtuple

Test = namedtuple("Test", ["n", "from_base", "to_base", "expected"])


def convert(n: int, from_base: int, to_base: int) -> int:
    digits = []
    while n:
        (n, r) = divmod(n, to_base)
        digits.append(r)    
    return sum(from_base ** i * v for i, v in enumerate(digits))


if __name__ == "__main__":
    tests = [
        Test(32, 16, 10, 50),
        Test(32, 20, 10, 62),
        Test(1010, 2, 10, 10),
        Test(8, 10, 8, 10),
        Test(150, 100, 1000, 150),
        Test(1500, 100, 10, 1050000),
    ]

    for test in tests:
        result = convert(*test[:-1])
        assert result == test.expected, f"{test=}, {result=}"
    print("PASSED!!!")
Handed answered 1/2, 2020 at 10:36 Comment(0)
C
0

Say we want to convert 14 to base 2. We repeatedly apply the division algorithm until the quotient is 0:

14 = 2 x 7

7 = 2 x 3 + 1

3 = 2 x 1 + 1

1 = 2 x 0 + 1

The binary representation is just the remainder read from bottom to top. This can be proved by expanding

14 = 2 x 7 = 2 x (2 x 3 + 1) = 2 x (2 x (2 x 1 + 1) + 1) = 2 x (2 x (2 x (2 x 0 + 1) + 1) + 1) = 2^3 + 2^2 + 2

The code is the implementation of the above algorithm.

def toBaseX(n, X):
strbin = ""
while n != 0:
    strbin += str(n % X)
    n = n // X
return strbin[::-1]
Chiton answered 24/6, 2021 at 10:42 Comment(2)
"They can't go to eleven" -- Nigel from "This is Spinal Tap"Osier
@MarkBorgerding Yeah. But it should be easy to implement larger bases.Chiton
J
0

This is my approach. At first converting the number then casting it to string.

    def to_base(n, base):
        if base == 10:
            return n
        
        result = 0
        counter = 0
        
        while n:
            r = n % base
            n //= base
            result += r * 10**counter
            counter+=1
        return str(result)
Jaine answered 10/7, 2021 at 4:52 Comment(0)
C
0

I have written this function which I use to encode in different bases. I also provided the way to shift the result by a value 'offset'. This is useful if you'd like to encode to bases above 64, but keeping displayable chars (like a base 95).

I also tried to avoid reversing the output 'list' and tried to minimize computing operations. The array of pow(base) is computed on demand and kept for additional calls to the function.

The output is a binary string

pows = {}

######################################################
def encode_base(value,
                base = 10,
                offset = 0) :

    """
    Encode value into a binary string, according to the desired base.

    Input :
        value : Any positive integer value
        offset : Shift the encoding (eg : Starting at chr(32))
        base : The base in which we'd like to encode the value

    Return : Binary string

    Example : with : offset = 32, base = 64

              100 -> !D
              200 -> #(
    """

    # Determine the number of loops
    try :
        pb = pows[base]

    except KeyError :
        pb = pows[base] = {n : base ** n for n in range(0, 8) if n < 2 ** 48 -1}

    for n in pb :
        if value < pb[n] :
            n -= 1
            break

    out = []
    while n + 1 :
        b = pb[n]
        out.append(chr(offset + value // b))
        n -= 1
        value %= b

    return ''.join(out).encode()
Comfort answered 18/8, 2021 at 19:25 Comment(0)
B
0

This function converts any integer from any base to any base

def baseconvert(number, srcbase, destbase):
    if srcbase != 10:
        sum = 0
        for _ in range(len(str(number))):
            sum += int(str(number)[_]) * pow(srcbase, len(str(number)) - _ - 1)
        b10 = sum
        return baseconvert(b10, 10, destbase)
    end = ''
    q = number
    while(True):
        r = q % destbase
        q = q // destbase
        end = str(r) + end
        if(q<destbase):
            end = str(q) + end
            return int(end)
Bronwen answered 27/12, 2021 at 7:12 Comment(0)
L
0

The below provided Python code converts a Python integer to a string in arbitrary base ( from 2 up to infinity ) and works in both directions. So all the created strings can be converted back to Python integers by providing a string for N instead of an integer. The code works only on positive numbers by intention (there is in my eyes some hassle about negative values and their bit representations I don't want to dig into). Just pick from this code what you need, want or like, or just have fun learning about available options. Much is there only for the purpose of documenting all the various available approaches ( e.g. the Oneliner seems not to be fast, even if promised to be ).

I like the by Salvador Dali proposed format for infinite large bases. A nice proposal which works optically well even for simple binary bit representations. Notice that the width=x padding parameter in case of infiniteBase=True formatted string applies to the digits and not to the whole number. It seems, that code handling infiniteBase digits format runs even a bit faster than the other options - another reason for using it?

I don't like the idea of using Unicode for extending the number of symbols available for digits, so don't look in the code below for it, because it's not there. Use the proposed infiniteBase format instead or store integers as bytes for compression purposes.

    def inumToStr( N, base=2, width=1, infiniteBase=False,\
    useNumpy=False, useRecursion=False, useOneliner=False, \
    useGmpy=False, verbose=True):
    ''' Positive numbers only, but works in BOTH directions.
    For strings in infiniteBase notation set for bases <= 62 
    infiniteBase=True . Examples of use:
    inumToStr( 17,  2, 1, 1)             # [1,0,0,0,1]
    inumToStr( 17,  3, 5)                #       00122
    inumToStr(245, 16, 4)                #        00F5
    inumToStr(245, 36, 4,0,1)            #        006T
    inumToStr(245245245245,36,10,0,1)    #  0034NWOQBH
    inumToStr(245245245245,62)           #     4JhA3Th 
        245245245245 == int(gmpy2.mpz('4JhA3Th',62))
    inumToStr(245245245245,99,2) # [25,78, 5,23,70,44]
    ----------------------------------------------------
    inumToStr( '[1,0,0,0,1]',2, infiniteBase=True ) # 17 
    inumToStr( '[25,78, 5,23,70,44]', 99) # 245245245245
    inumToStr( '0034NWOQBH', 36 )         # 245245245245 
    inumToStr( '4JhA3Th'   , 62 )         # 245245245245
    ----------------------------------------------------
    --- Timings for N = 2**4096, base=36: 
                                      standard: 0.0023
                                      infinite: 0.0017
                                      numpy   : 0.1277
                                      recursio; 0.0022
                                      oneliner: 0.0146
                For N = 2**8192: 
                                      standard: 0.0075
                                      infinite: 0.0053
                                      numpy   : 0.1369
    max. recursion depth exceeded:    recursio/oneliner
    '''
    show = print
    if type(N) is str and ( infiniteBase is True or base > 62 ):
        lstN = eval(N)
        if verbose: show(' converting a non-standard infiniteBase bits string to Python integer')
        return sum( [ item*base**pow for pow, item in enumerate(lstN[::-1]) ] )
    if type(N) is str and base <= 36:
        if verbose: show('base <= 36. Returning Python int(N, base)')
        return int(N, base)
    if type(N) is str and base <= 62:
        if useGmpy: 
            if verbose: show(' base <= 62, useGmpy=True, returning int(gmpy2.mpz(N,base))')
            return int(gmpy2.mpz(N,base))
        else:
            if verbose: show(' base <= 62, useGmpy=False, self-calculating return value)')
            lstStrOfDigits="0123456789"+ \
                "abcdefghijklmnopqrstuvwxyz".upper() + \
                "abcdefghijklmnopqrstuvwxyz"
            dictCharToPow = {}
            for index, char in enumerate(lstStrOfDigits):
                dictCharToPow.update({char : index}) 
            return sum( dictCharToPow[item]*base**pow for pow, item in enumerate(N[::-1]) )
        #:if
    #:if        
        
    if useOneliner and base <= 36:  
        if verbose: show(' base <= 36, useOneliner=True, running the Oneliner code')
        d="0123456789abcdefghijklmnopqrstuvwxyz"
        baseit = lambda a=N, b=base: (not a) and d[0]  or \
        baseit(a-a%b,b*base)+d[a%b%(base-1) or (a%b) and (base-1)]
        return baseit().rjust(width, d[0])[1:]

    if useRecursion and base <= 36: 
        if verbose: show(' base <= 36, useRecursion=True, running recursion algorythm')
        BS="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        def to_base(n, b): 
            return "0" if not n else to_base(n//b, b).lstrip("0") + BS[n%b]
        return to_base(N, base).rjust(width,BS[0])
        
    if base > 62 or infiniteBase:
        if verbose: show(' base > 62 or infiniteBase=True, returning a non-standard digits string')
        # Allows arbitrary large base with 'width=...' 
        # applied to each digit (useful also for bits )
        N, digit = divmod(N, base)
        strN = str(digit).rjust(width, ' ')+']'
        while N:
            N, digit = divmod(N, base)
            strN = str(digit).rjust(width, ' ') + ',' + strN
        return '[' + strN
    #:if        
    
    if base == 2:
        if verbose: show(" base = 2, returning Python str(f'{N:0{width}b}')")
        return str(f'{N:0{width}b}')
    if base == 8:
        if verbose: show(" base = 8, returning Python str(f'{N:0{width}o}')")
        return str(f'{N:0{width}o}')
    if base == 16:
        if verbose: show(" base = 16, returning Python str(f'{N:0{width}X}')")
        return str(f'{N:0{width}X}')

    if base <= 36:
        if useNumpy: 
            if verbose: show(" base <= 36, useNumpy=True, returning np.base_repr(N, base)")
            import numpy as np
            strN = np.base_repr(N, base)
            return strN.rjust(width, '0') 
        else:
            if verbose: show(' base <= 36, useNumpy=False, self-calculating return value)')
            lstStrOfDigits="0123456789"+"abcdefghijklmnopqrstuvwxyz".upper()
            strN = lstStrOfDigits[N % base] # rightmost digit
            while N >= base:
                N //= base # consume already converted digit
                strN = lstStrOfDigits[N % base] + strN # add digits to the left
            #:while
            return strN.rjust(width, lstStrOfDigits[0])
        #:if
    #:if
    
    if base <= 62:
        if useGmpy: 
            if verbose: show(" base <= 62, useGmpy=True, returning gmpy2.digits(N, base)")
            import gmpy2
            strN = gmpy2.digits(N, base)
            return strN.rjust(width, '0') 
            # back to Python int from gmpy2.mpz with 
            #     int(gmpy2.mpz('4JhA3Th',62))
        else:
            if verbose: show(' base <= 62, useGmpy=False, self-calculating return value)')
            lstStrOfDigits= "0123456789" + \
                "abcdefghijklmnopqrstuvwxyz".upper() + \
                "abcdefghijklmnopqrstuvwxyz"
            strN = lstStrOfDigits[N % base] # rightmost digit
            while N >= base:
                N //= base # consume already converted digit
                strN = lstStrOfDigits[N % base] + strN # add digits to the left
            #:while
            return strN.rjust(width, lstStrOfDigits[0])
        #:if
    #:if    
#:def
Lidalidah answered 8/2, 2022 at 1:17 Comment(0)
N
0

I'm presenting a "unoptimized" solution for bases between 2 and 9:

  def to_base(N, base=2):
    N_in_base = ''
    while True:
        N_in_base = str(N % base) + N_in_base
        N //= base
        if N == 0:
            break
    return N_in_base

This solution does not require reversing the final result, but it's actually not optimized. Refer to this answer to see why: https://mcmap.net/q/100622/-time-complexity-of-string-concatenation-in-python-duplicate

Normally answered 4/3, 2022 at 18:6 Comment(0)
T
0

Simple base transformation

def int_to_str(x, b):
    s = ""
    while x:
        s = str(x % b) + s
        x //= b
    return s

Example of output with no 0 to base 9

s = ""
x = int(input())
while x:
    if x % 9 == 0:
        s = "9" + s
        x -= x % 10
        x = x // 9
    else:
        s = str(x % 9) + s
        x = x // 9

print(s)
Tacnaarica answered 27/3, 2022 at 10:49 Comment(0)
U
0

Here's some code I wrote to help someone who was looking to Speedily calculate base 3 value of real huge integer number. It turns out the way to do that is to make a function that works with any base and have it call itself recursively. This takes advantage of a few properties:

  • Python can work with integers of unlimited size and perform all the usual arithmetic operations on them.
  • Converting from base**2 to base is easy, because each digit in base**2 becomes two digits in base using a simple division and remainder.
  • If the base is larger than the number you're trying to convert, the answer is trivial - the number is the single digit.
def number_to_base(n, base):
    if n < base:
        return [n]
    else:
        digits = [d for x in number_to_base(n, base*base) for d in divmod(x, base)]
        return digits if digits[0] else digits[1:]

This returns a list of numeric digits. To convert it to a string that can be interpreted by int(s, base) is quite simple:

def int2base(x, base, alphabet='0123456789abcdefghijklmnopqrstuvwxyz'):
    return ''.join(alphabet[digit] for digit in number_to_base(x, base))

This only handles positive numbers, but making it handle negative numbers is trivial - I leave that as an exercise for the reader.

This function is actually faster than Python's str() function for base 10 if the number is large enough. In my tests they were equal at about 100000 digits.

Unreel answered 28/11, 2023 at 4:12 Comment(0)
E
-1
def dec_to_radix(input, to_radix=2, power=None):
    if not isinstance(input, int):
        raise TypeError('Not an integer!')
    elif power is None:
        power = 1

    if input == 0:
        return 0
    else:
        remainder = input % to_radix**power
        digit = str(int(remainder/to_radix**(power-1)))
        return int(str(dec_to_radix(input-remainder, to_radix, power+1)) + digit)

def radix_to_dec(input, from_radix):
    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]))

def radix_to_radix(input, from_radix=10, to_radix=2, power=None):
    dec = radix_to_dec(input, from_radix)
    return dec_to_radix(dec, to_radix, power)
Employ answered 10/8, 2015 at 22:1 Comment(0)
P
-1
def baseConverter(x, b):
    s = ""
    d = string.printable.upper()
    while x > 0:
        s += d[x%b]
        x = x / b
    return s[::-1]
Phytophagous answered 23/1, 2016 at 20:41 Comment(1)
For python3 your code does this: baseConverter(0, 26) -> '' baseConverter(1, 26) -> '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001' For python2 it does this: baseConverter(0, 26) -> '' baseConverter(1, 26) -> 1 baseConverter(3, 26) -> 3 baseConverter(5, 26) -> 5 baseConverter(26, 26) -> 10 baseConverter(32, 26) -> 16Hawkins
S
-1

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")
Silverts answered 26/1, 2016 at 14:5 Comment(0)
B
-1

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[0]
    sign = '-' if x < 0 else ''
    x = abs(x)
    first_digits = base_convert(x // base, base, digits).lstrip(digits[0])
    return sign + first_digits + digits[x % base]
Basinet answered 9/12, 2016 at 8:53 Comment(0)
C
-1
num = input("number")
power = 0
num = int(num)
while num > 10:
    num = num / 10
    power += 1

print(str(round(num, 2)) + "^" + str(power))
Chromium answered 15/11, 2018 at 4:9 Comment(2)
please add some brief information that what you did special initLibration
While this might answer the authors question, it lacks some explaining words and/or links to documentation. Raw code snippets are not very helpful without some phrases around them. You may also find how to write a good answer very helpful. Please edit your answer.Feldman
H
-1

Well I personally use this function, written by me

import string

def to_base(value, base, digits=string.digits+string.ascii_letters):    # converts decimal to base n

    digits_slice = digits[0:base]

    temporary_var = value
    data = [temporary_var]

    while True:
        temporary_var = temporary_var // base
        data.append(temporary_var)
        if temporary_var < base:
            break

    result = ''
    for each_data in data:
        result += digits_slice[each_data % base]
    result = result[::-1]

    return result

This is how you can use it

print(to_base(7, base=2))

Output: "111"

print(to_base(23, base=3))

Output: "212"

Please feel free to suggest improvements in my code.

Hangover answered 30/11, 2019 at 19:46 Comment(0)
E
-1

This is an old question but I thought i'd share my take on it as I feel it is somewhat simpler that other answers (good for bases from 2 to 36):

def intStr(n,base=10):
    if n < 0   : return "-" + intStr(-n,base)         # handle negatives
    if n < base: return chr([48,55][n>9] + n)         # 48 => "0"..., 65 => "A"...
    return intStr(n//base,base) + intStr(n%base,base) # recurse for multiple digits
Emory answered 29/4, 2020 at 16:36 Comment(0)
H
-1

I know this is an old post, but I'm just leaving my solution here just in case.

def decimal_to_given_base(integer_to_convert, base):
     remainder = integer_to_convert // base
     digit = integer_to_convert % base
     if integer_to_convert == 0:
         return '0'
     elif remainder == 0:
         return str(digit)
     else:
         return decimal_to_given_base(remainder, base) + str(digit)
Holds answered 14/7, 2020 at 14:15 Comment(0)
E
-1

I made my function to do this. Run well on windows 10, python 3.7.3.

def number_to_base(number, base, precision = 10):
    if number == 0:
        return [0]
    
    positive = number >= 0
    number = abs(number)
    
    ints = []  # store the integer bases
    floats = []  # store the floating bases

    float_point = number % 1
    number = int(number)
    while number:
        ints.append(int(number%base))
        number //= base
    ints.reverse()
    
    while float_point and precision:
        precision -= 1
        float_point *= base
        floats.append(int(float_point))
        float_point = float_point - int(float_point)

    return ints, floats, positive


def base_to_str(bases, string="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"):
    """bases is a two dimension list, where bases[0] contains a list of the integers,
    and bases[1] contains a list of the floating numbers, bases[2] is a boolean, that's
    true when it's a positive number
    """
    ints = []
    floats = []

    for i in bases[0]:
        ints.append(string[i])

    for i in bases[1]:
        floats.append(string[i])

    if len(bases[1]) > 0:
        return (["-", ""][bases[2]] + "".join(ints)) + "." + ("".join(floats))
    else:
        return (["-", ""][bases[2]] + "".join(ints))
    

    

Example:

>>> base_to_str(number_to_base(-6.252, 2))
'-110.0100000010'
Elenor answered 18/9, 2020 at 19:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.