tags:

views:

126

answers:

2

I've run into something odd, here, and I'm at a loss -- I have a feeling this has something to do with floating precision, but I'm surprised Python would not display the approximation error, if so.

I'm working on Project Euler problem 62. As a simple test (I've since solved using a different approach), I had a is_cube which I intended to check if a number cubes perfectly. So, to try the sample given, I did this:

def is_cube(i):
    c = i ** (1./3)
    print "c is", c

When I ran this with i = 41063625, the expected output was:

c is 345.0

Here's where the unexpected happened:

def is_cube(i):
    c = i ** (1./3)
    print "c is", int(c)

Suddenly, I had this:

c is 344

The value c does not compare against my 345.0 literal, either -- c < 345.0 is True.

Is there precision in this number that Python is not showing me? I seem to recall reading about a change to make floats appear more sane when printed; is this it? What is different about these two cases?

>>> def is_cube(i):
...     c = i ** (1./3)
...     print "c is", c
... 
>>> is_cube(41063625)
c is 345.0
>>> 41063625 ** (1./3)
344.99999999999989

Edit: Still had the window open and did this:

>>> print _
345.0

Now I'm starting to think I should have known all along that print was to blame.

+4  A: 

This is using c.__str__() (aka. str(c)) :

print "c is", c

This is using c.__repr__() (aka. repr(c)) :

>>> c # In the Python shell

IIRC, __str__ truncates to 10 decimals, whereas __repr__ goes further. To get the same behavior as in the Python shell, you could do :

print repr(c)
# Or
print "%r" % c
# Or
print "%.16f" % c

The change you're talking about in your message is only about Python 3.1 and do not alter the precision of the output : Gay's algorithm which is used for __repr__ on floating point numbers in Python 3.1 will, when given two representation choices which yields the same floating point value (like 0.2 and 0.2000000000000001), choose the shortest one.

Pierre Bourdon
Ah, thank you for clarifying the change, as well. Don't you hate when you read something that might be remotely relevant, file it away, and later your long-term memory starts going *hey! hey! I know that!* but you can't remember why?
Jed Smith
A: 
    print "c is", int(c)

The integer conversion will simply drop anything after the decimal point so even if you have 344.99999999999989 it will always round down. you could use old school print %

 print'c is %5.0f' % c

or you could use round if you want an integer

print'c is %d' % round(c,0)

Also you might be interested in other solutions for the cube root problem here

10ToedSloth
If you're gonna minus at least state the reason
10ToedSloth
Cheers Craig, but still curious about the down vote. Like to know why someone thought my answer was so off the mark. So if anyone's got some constructive criticism its appreciated.
10ToedSloth
I'm the one who downvoted : your answer does not bring anything that the OP did not know, the problem was about the precision of `str`, etc. and not about floating point arith which the OP seems to know.
Pierre Bourdon
@ Pierre Bourdon: Did you read the part where I said the conversion to an int will always result in rounddown. This is precisely the reason Jed got the result 344 when he expressed he expected 345.
10ToedSloth
The OP knows that. Read the third comment on the question, where he says he expected to get this result.
Pierre Bourdon