3

I'm trying to convert a decimal number to an arbitrary base and back to decimal. I found this code below from another question:

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

When I convert a decimal to hex:

in_base16 = int2base(number, 16)

it works, but when I try to convert that result back to decimal (base 10):

back_to_10 = int2base(in_base16, 10)

... it gives me the error:

    if x<=0:
    TypeError: '<=' not supported between instances of 'str' and 'int'

It can't convert a string back to a number for some reason. I don't understand why. How would I convert a number of an arbitrary base back to decimal?

Community
  • 1
  • 1
Un1
  • 3,874
  • 12
  • 37
  • 79
  • `"0x10" <= 0` is something python does not understand... try `int(in_base16,16)` – Joran Beasley May 16 '17 at 18:09
  • The function you are using converts a number from an integer to its string representation in another base. By passing its output to itself, you are now passing a string to a function that only accepts integers. You need to use the built-in `int(string, base)` function to convert your number to an integer before putting it into the function you have. – Random Davis May 16 '17 at 18:09
  • @RandomDavis Oh, yeah, I see now. But is there an easy way to convert that representation of a number back to decimal? – Un1 May 16 '17 at 18:10
  • Yes, it's built-in to python: `int(string, base)`. You would just have to change your second function call to `back_to_10 = int2base(int(in_base16, 16), 10)`. I converted this comment into an answer if that indeed is all you needed. – Random Davis May 16 '17 at 18:11

4 Answers4

1

Your basic problem is the signature: your function works only on x as an integer. Instead, you need either a second function that works on strings, or a type check at the top of this routine to detect the input type.

The basic problem is that you've assumed that you can get the digit's value within the radix (base) with a simple reference; this works for an integer, but not a string. "9" doesn't yield a numeric value of 9; "B" doesn't give you 11.

Instead, you need to feed the character to the index function and take the return value:

digit_value = alphabet.index(char)

will give you the character's position in the alphabet, which is the digital value you want for your computations.

Can you take it from there?

Prune
  • 76,765
  • 14
  • 60
  • 81
  • Yeah, I understand that now. The other answers works fine, but the built-in function doesn't work for base >36. I'm going to try your method now. Thanks – Un1 May 16 '17 at 18:26
  • Great to hear. Also check your alphabet for base-64, in case there's some standard that matters. The one I used in ancient times started with the decimal digits. – Prune May 16 '17 at 18:29
  • BTW, you can find the code for this solution in many places on line, if a pre-packaged solution would do the trick. – Prune May 16 '17 at 18:29
  • I tried to find the solution on stackoverflow (via google) but I couldn't find the way to convert it back to decimal. Can't quite do it myself yet. I've already got 2 errors, trying to add your line of code in the function, ehh, newbie programmers... – Un1 May 16 '17 at 18:34
  • Hint from an aged programmer: write a separate function before you try to make this one do both jobs. – Prune May 16 '17 at 18:36
  • First, make it work; next, make it work well; finally, make it pretty. – Prune May 16 '17 at 18:36
  • Thanks for the advice. I got your line of code working as a separate function, it shows the index of a char in the alphabet. So now I just put this in a `for` loop and it's going to replace all the letters with numbers right? And it's going to work for any base too, right? – Un1 May 16 '17 at 18:45
  • There's are examples [here](http://www.itgo.me/a/3456787421052264894/how-to-convert-an-integer-to-the-shortest-url-safe-string-in-python) that do the job. – Prune May 16 '17 at 18:45
  • It gives you the decimal value for each position. You still have to do the base conversion, that multiply-and-shift thing. – Prune May 16 '17 at 18:46
1

Your int2base function only accepts an integer as input, so you need to convert your string to an integer. So, you can simply use the built-in function int(string, base) to do this:

in_base16 = int2base(number, 16)
back_to_10 = int2base(int(in_base16, 16), 10)

Which works fine.

Random Davis
  • 6,662
  • 4
  • 14
  • 24
  • Thank you. It works indeed. But it seems it only works for range 2 - 36 `int() base must be >= 2 and <= 36` – Un1 May 16 '17 at 18:22
  • Sounds like if you needed more than those bases, you would just have to rework your int2base function to go the other direction. – Random Davis May 16 '17 at 19:06
1

If base-n is up to 36, you can use the built-in int(str,base).

>>> int('AA', 32)
330
Vinícius Figueiredo
  • 6,300
  • 3
  • 25
  • 44
1

You'll need one function for converting from int to base (returning a string that represents a number in that base -- here up to base 36):

digits = '0123456789abcdefghijklmnopqrstuvwxyz'

def int2base(n, b=2, digits=digits):
    "convert integer n to base b"
    if n < 0:
        raise ValueError("no negative numbers")
    if n < b:
        return digits[n]
    res = []
    q = n
    while q:
        q, r = divmod(q, b)
        res.append(digits[r])
    return ''.join(reversed(res))

and another function for converting back from base to int:

def base2int(s, base=2, digits=digits):
    "convert string s representing a number in base to int (base 10)"
    if not (2 <= base <= len(digits)):
        raise ValueError("base must be >= 2 and <= %d" % len(digits))
    res = 0
    for i, v in enumerate(reversed(s)):
        digit = digits.index(v)
        res += digit * (base ** i)
    return res
thebjorn
  • 26,297
  • 11
  • 96
  • 138