How to Convert an Integer to a String in Any Base

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

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)

Is there a built in function to change the base of an integer in Python?

When you speak of an integer object, it is not the same as its base N representation.

While internally, the integer is stored in binary, the integer object itself has no base. You can construct an int object from any string of digits in any base N, as long as you specify the base and the string of digits is valid for that base. This is usually restricted to a maximum of 36, since there are 10 decimal digits and 26 letters in the alphabet.

There are special cases for the bases 2, 8, 10, and 16, since they are the most used. For those bases, you can specify the base within the string representation itself. You use prefixes of 0b, 0o, or 0x for 2, 8, and 16 respectively. Decimal is the default and the most used, so it requires no prefix.

If you want to return the base N representation of an integer as a string, i.e. invert the operation int(n, N), you have several options. You can use gmpy.digits(n, N) where n is your int, and N is the base you want. This might be overkill, since it requires installing gmpy.

You can alternatively use something like this:

import string

def int_to_base(n, N):
""" Return base N representation for int n. """
base_n_digits = digits + ascii_lowercase + ascii_uppercase
result = ""
if n < 0:
sign = "-"
n = -n
else:
sign = ""
while n > 0:
q, r = divmod(n, N)
result += base_n_digits[r]
n = q
if result == "":
result = "0"
return sign + "".join(reversed(result))

With that function, or something similar to it, you can return the string base N representation of an integer n in base N, so that:

>>> base_n_string = "81"
>>> base = 9
>>> int_to_base(int("81", 9), 9) == "81"
True

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

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)

Converting a base 10 number to any base without strings

a simple implementation which can "convert" up to base 9 is as follows:

def conver(n,b):
a = 0
i = 0
while n:
n,r = divmod(n,b)
a += 10**i * r
i += 1

return a

note that n,r = divmod(n,b) could be r = n % b; n //= b

Note that the number you're getting is an integer all right (as a type), but it makes no sense using it as such because the base is wrong: Ex: 1110 is not really 1110. So it's not really different than a string...

Well, that's just for the sake of the exercise.

Python 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. :)

Is there a built-in function that converts a number to a string in any base?

If you wanted to eke out a little more performance, you can create a struct and implement Display or Debug for it. This avoids allocating a String. For maximum over-engineering, you can also have a stack-allocated array instead of the Vec.

Here is Boiethios' answer with these changes applied:

struct Radix {
x: i32,
radix: u32,
}

impl Radix {
fn new(x: i32, radix: u32) -> Result<Self, &'static str> {
if radix < 2 || radix > 36 {
Err("Unnsupported radix")
} else {
Ok(Self { x, radix })
}
}
}

use std::fmt;

impl fmt::Display for Radix {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut x = self.x;
// Good for binary formatting of `u128`s
let mut result = ['\0'; 128];
let mut used = 0;
let negative = x < 0;
if negative {
x*=-1;
}
let mut x = x as u32;
loop {
let m = x % self.radix;
x /= self.radix;

result[used] = std::char::from_digit(m, self.radix).unwrap();
used += 1;

if x == 0 {
break;
}
}

if negative {
write!(f, "-")?;
}

for c in result[..used].iter().rev() {
write!(f, "{}", c)?;
}

Ok(())
}
}

fn main() {
assert_eq!(Radix::new(1234, 10).to_string(), "1234");
assert_eq!(Radix::new(1000, 10).to_string(), "1000");
assert_eq!(Radix::new(0, 10).to_string(), "0");
}

This could still be optimized by:

  • creating an ASCII array instead of a char array
  • not zero-initializing the array

Since these avenues require unsafe or an external crate like arraybuf, I have not included them. You can see sample code in internal implementation details of the standard library.



Related Topics



Leave a reply



Submit