string.atoi and string.atol broken?

M

Mike Moum

I think there may be a bug in string.atoi and string.atol. Here's some
output from idle.
Python 2.3.4 (#2, Jan 5 2005, 08:24:51)
[GCC 3.3.5 (Debian 1:3.3.5-5)] on linux2
Type "copyright", "credits" or "license()" for more information.

****************************************************************
Personal firewall software may warn about the connection IDLE
makes to its subprocess using this computer's internal loopback
interface. This connection is not visible on any external
interface and no data is sent to or received from the Internet.
****************************************************************

IDLE 1.0.4
Traceback (most recent call last):
File "<pyshell#2>", line 1, in -toplevel-
s.atoi('4',3)
File "/usr/lib/python2.3/string.py", line 220, in atoi
return _int(s, base)
ValueError: invalid literal for int(): 4
Traceback (most recent call last):
File "<pyshell#6>", line 1, in -toplevel-
s.atoi('8',4)
File "/usr/lib/python2.3/string.py", line 220, in atoi
return _int(s, base)
ValueError: invalid literal for int(): 8

s.atoi('4',3) should result in 11

s.atoi('13',4) should result in 31

s.atoi('12',4) should result in 30

s.atoi('8',4) is legitimate, but it generates an error.

Is this a bug, or am I missing something obvious?

TIA,
Mike
 
D

Dennis Benzinger

Mike said:
I think there may be a bug in string.atoi and string.atol. Here's some
output from idle.

Python 2.3.4 (#2, Jan 5 2005, 08:24:51)
[GCC 3.3.5 (Debian 1:3.3.5-5)] on linux2
Type "copyright", "credits" or "license()" for more information.

****************************************************************
Personal firewall software may warn about the connection IDLE
makes to its subprocess using this computer's internal loopback
interface. This connection is not visible on any external
interface and no data is sent to or received from the Internet.
****************************************************************

IDLE 1.0.4
import string as s
s.atoi('2',3)
2

s.atoi('4',3)

Traceback (most recent call last):
File "<pyshell#2>", line 1, in -toplevel-
s.atoi('4',3)
File "/usr/lib/python2.3/string.py", line 220, in atoi
return _int(s, base)
ValueError: invalid literal for int(): 4
s.atoi('12',11)
13


7


6

s.atoi('8',4)

Traceback (most recent call last):
File "<pyshell#6>", line 1, in -toplevel-
s.atoi('8',4)
File "/usr/lib/python2.3/string.py", line 220, in atoi
return _int(s, base)
ValueError: invalid literal for int(): 8

s.atoi('4',3) should result in 11

s.atoi('13',4) should result in 31

s.atoi('12',4) should result in 30

s.atoi('8',4) is legitimate, but it generates an error.

Is this a bug, or am I missing something obvious?
[...]

That's not a bug, you'r missing something obvious.

The second parameter of string.atoi (or the int builtin)
is the base (or radix) in which the number you want to
convert is given.

For example string.atoi("777", 8) results in 511,
because 7 * 8**2 + 7 * 8**1 + 7 * 8**0 = 511.

Just out of curiosty:
What did you think what atoi does?
I don't understand how you came to expect that atoi('4',3)
should result in 11.


Bye,
Dennis
 
S

Steven Bethard

What did you expect the value of '4' in base 3 to be? There is no '4'
in base 3... only '0', '1' and '2'.

And no '8' in base 3 either.

Is this a bug, or am I missing something obvious?

Well, first of all, unless you're using a Python before 2.0, you're
missing that string.atoi is deprecated. You should be using int(),
which can take the same parameters.

I think secondly, you're missing that int(string, base) converts the
given string to an int assuming that the string is in the given base.
If the numbers you provide are out of the range of the digits that are
valid for that base, you will get an error.

Steve
 
P

Peter Otten

Mike said:
s.atoi('4',3) should result in 11

s.atoi('13',4) should result in 31

s.atoi('12',4) should result in 30

s.atoi('8',4) is legitimate, but it generates an error.

Is this a bug, or am I missing something obvious?

You and atoi() seem to disagree about the direction of the conversion, and
atoi() wins :). It converts a string representation of a number into the
corresponding integer. The second parameter specifies in what base this
string is given.
You seem to want something like

import string

def itoa(n, base):
assert 2 <= base <= 16
if n < 0:
digits = ["-"]
n = -n
else:
digits = []
while n:
n, m = divmod(n, base)
digits.append(string.hexdigits[m])
digits.reverse()
return "".join(digits)

if __name__ == "__main__":
assert itoa(4, 3) == "11"
assert itoa(13, 4) == "31"
assert itoa(12, 4) == "30"
assert itoa(8, 4) == "20"


Peter
 
D

Dan Bishop

Peter said:
You and atoi() seem to disagree about the direction of the conversion, and
atoi() wins :). It converts a string representation of a number into the
corresponding integer. The second parameter specifies in what base this
string is given.
You seem to want something like

import string

def itoa(n, base):
assert 2 <= base <= 16

Why have the restriction base <= 16? int() allows up to 36. All you
need to do is

BASE36_DIGITS = string.digits + string.lowercase

and change
digits.append(string.hexdigits[m]) to

digits.append(BASE36_DIGITS[m])
 
M

Mike Moum

Dennis said:
Just out of curiosty:
What did you think what atoi does?
I don't understand how you came to expect that atoi('4',3)
should result in 11.


Bye,
Dennis

Mea culpa. For some strange reason, I had it in my mind that atoi would
take a base ten number as a string and convert it to the correct
representation in the base of the second argument. In other words,
atoi('4',3) should take 4 in base 10 and convert it to base 3, resulting
in 11. Exactly backwords, as atoi('11',3) = 4, that is, 11 base 3 = 4
base 10.

Thanks to all for setting me straight.

Mike
 
F

Fredrik Lundh

Mike said:
s.atoi('4',3) should result in 11

s.atoi('13',4) should result in 31

s.atoi('12',4) should result in 30

s.atoi('8',4) is legitimate, but it generates an error.

Is this a bug, or am I missing something obvious?

the function's named "atoi", not "atoitoa".

</F>
 
N

Nick Coghlan

Peter said:
Mike Moum wrote:

s.atoi('4',3) should result in 11

s.atoi('13',4) should result in 31

s.atoi('12',4) should result in 30

s.atoi('8',4) is legitimate, but it generates an error.

Is this a bug, or am I missing something obvious?


You and atoi() seem to disagree about the direction of the conversion, and
atoi() wins :). It converts a string representation of a number into the
corresponding integer. The second parameter specifies in what base this
string is given.
You seem to want something like

import string

def itoa(n, base):
assert 2 <= base <= 16
if n < 0:
digits = ["-"]
n = -n
else:
digits = []
while n:
n, m = divmod(n, base)
digits.append(string.hexdigits[m])
digits.reverse()
return "".join(digits)

if __name__ == "__main__":
assert itoa(4, 3) == "11"
assert itoa(13, 4) == "31"
assert itoa(12, 4) == "30"
assert itoa(8, 4) == "20"

Huh - you remind me that I forgot to put the "show_base" Bengt and I came up
with into the ASPN cookbook. . .

Py> def show_base(val, base, min_digits=1, complement=False,
.... digits="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"):
.... if base > len(digits): raise ValueError("Not enough digits for base")
.... negative = val < 0
.... val = abs(val)
.... if complement:
.... sign = ""
.... max = base**min_digits
.... if (val >= max) or (not negative and val == max):
.... raise ValueError("Value out of range for complemented format")
.... if negative:
.... val = (max - val)
.... else:
.... sign = "-" * negative
.... val_digits = []
.... while val:
.... val, digit = divmod(val, base)
.... val_digits.append(digits[digit])
.... result = "".join(reversed(val_digits))
.... return sign + ("0" * (min_digits - len(result))) + result
....
Py> show_base(10, 2)
'1010'
Py> show_base(-10, 2)
'-1010'
Py> show_base(10, 2, 8)
'00001010'
Py> show_base(-10, 2, 8)
'-00001010'
Py> show_base(10, 2, 8, complement=True)
'00001010'
Py> show_base(-10, 2, 8, complement=True)
'11110110'
Py> show_base(10, 16, 2, complement=True)
'0A'
Py> show_base(-10, 16, 2, complement=True)
'F6'
Py> show_base(127, 16, 2, complement=True)
'7F'
Py> show_base(-127, 16, 2, complement=True)
'81'
Py> show_base(255, 16, 2, complement=True)
'FF'
Py> show_base(-255, 16, 2, complement=True)
'01'
Py> show_base(256, 16, 2, complement=True)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 10, in show_base
ValueError: Value out of range for complemented format
Py> show_base(-256, 16, 2, complement=True)
'00'
Py>
 
P

Peter Otten

Dan said:
Why have the restriction base <= 16?  int() allows up to 36.  All you
need to do is

BASE36_DIGITS = string.digits + string.lowercase

For no other reason than carelessness. I have not yet seen anything beyond
base-16 in the wild.

By the way, does anyone know the Greek name for 36?

Peter
 
P

Peter Otten

Nick said:
Huh - you remind me that I forgot to put the "show_base" Bengt and I came
up with into the ASPN cookbook. . .

Py> def show_base(val, base, min_digits=1, complement=False,
...               digits="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"):
...   if base > len(digits): raise ValueError("Not enough digits for
base") ...   negative = val < 0
...   val = abs(val)
...   if complement:
...     sign = ""
...     max = base**min_digits
...     if (val >= max) or (not negative and val == max):
...       raise ValueError("Value out of range for complemented format")
...     if negative:
...       val = (max - val)
...   else:
...     sign = "-" * negative
...   val_digits = []
...   while val:
...     val, digit = divmod(val, base)
...     val_digits.append(digits[digit])
...   result = "".join(reversed(val_digits))
...   return sign + ("0" * (min_digits - len(result))) + result
...

Yes, that is a bit more general. For the cookbook you might consider
factoring out the "".join() operation, thus entirely removing the upper
limit for the base (the output of the first step would be a tuple of
integers).

Peter
 
C

Christos TZOTZIOY Georgiou

By the way, does anyone know the Greek name for 36?

triakontahexadecimal would be a nice compromise of greek and the
"hexadecimal" convention of having six before ten -- "έξι" ("hexi") is
six, "δέκα" ("deka") is ten, "Ï„Ïιάκοντα" ("triakonta") is thirty. I
think in ancient Greek sometimes units came before tens, just like in
German (another similarity is the verb in the end of the sentence, as
Mark Twain also noted sometime in a humourous article AFAIR.)

In current Greek hexadecimal is "δεκαεξαδικόν" ("dekaexadikon").
 
C

Christos TZOTZIOY Georgiou

Mike Moum wrote:
the function's named "atoi", not "atoitoa".

<automatic_effbot_post_translator_powered_by_python>

s.itoa(4,3) should result in '11'
s.itoa(13,4) should result in '31'
s.itoa(12,4) should result in '30'
s.itoa(8,4) should result in '20'

s.atoi('4', 3) should fail
s.atoi('13', 4) should result in 7
s.atoi('12', 4) should result in 6
s.atoi('8', 4) should fail

</automatic_effbot_post_translator_powered_by_python> :)
 
P

Peter Otten

Peter said:
def itoa(n, base):
assert 2 <= base <= 16
if n < 0:
digits = ["-"]
n = -n
else:
digits = []
while n:
n, m = divmod(n, base)
digits.append(string.hexdigits[m])
digits.reverse()
return "".join(digits)

This is junk, sorry. Doesn't handle n<=0 correctly (at least).

Peter
 
P

Peter Otten

Christos said:
triakontahexadecimal would be a nice compromise of greek and the
"hexadecimal" convention of having six before ten -- "???" ("hexi") is
six, "????" ("deka") is ten, "?????????" ("triakonta") is thirty. I
think in ancient Greek sometimes units came before tens, just like in
German (another similarity is the verb in the end of the sentence, as
Mark Twain also noted sometime in a humourous article AFAIR.)

In current Greek hexadecimal is "????????????" ("dekaexadikon").

The Latin part escaped me. Now we need unicode names in Python, and the fun
can really begin.

I had you in mind with my question, thank you.

Peter
 
F

Fredrik Lundh

Christos said:
<automatic_effbot_post_translator_powered_by_python>

cool. can I have a copy of your script?

reminds me that I have a few patches in the inqueue. I wonder
what this one does? ;-)

hmm ;-) guess I can tune that later ;-) and what about that other
patch? ;-) let's see ;-) patch, checkout, reload File "effbot.py", line 29238
<<<<<<< .mine
^
IndentationError: expected an indented block
 
C

Christos TZOTZIOY Georgiou

[Fredrik] [0]
cool. can I have a copy of your script?

I hereby declare my script in perpetual alpha-testing stage, and
therefore won't be making it public, since I intend to make money out of
this [1] collecting all translations in a book called "The effbot's
guide to pythonology" [2].
reminds me that I have a few patches in the inqueue. I wonder
what this one does? ;-)
hmm ;-) guess I can tune that later ;-) and what about that other
patch? ;-) let's see ;-) patch, checkout, reload File "effbot.py", line 29238
<<<<<<< .mine
^
IndentationError: expected an indented block

You're messing with the time machine again, right? Your patch is
probably for nightly build 20071202032501 . At the moment, most of the
script logic (and bugs :) is concentrated in line 2:

what_to_print = raw_input("Translation of /F's single line reply? ")


[0] got it right this time

[1] Ska vi dela femtio-femtio?

[2] which book will help you improve your own python use, hopefully :)
 
P

Peter Otten

I borrowed Guido's time machine to fix the above (hopefully) :)
By Latin I suppose you jokingly mean the Greek parts... :)

I thought it was a well-known fact that speakers of the awful German
language have absolutely no humour.

My first assumption was that hexadecimal is Greek and then I modified that
to a Greek/Latin combo "έξαdecimal".
Just in case it didn't show up correctly in your ng/mail client, it does
on groups.google.com (the post was in UTF-8, dammit! --excuse my romance
language)

It did show up correctly, but was garbled in the response through technical
incompetence of the poster.

Peter
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,051
Latest member
CarleyMcCr

Latest Threads

Top