classification
Title: Make conversions from long to float correctly rounded.
Type: feature request Stage: patch review
Components: Interpreter Core Versions: Python 2.6
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: belopolsky, drj, haypo, marketdickinson
Priority: Keywords: patch

Created on 2008-06-21 21:11 by marketdickinson, last changed 2008-11-06 17:30 by belopolsky.

Files
File name Uploaded Description Edit Remove
long_as_double.patch marketdickinson, 2008-06-21 21:11
Messages
msg68545 (view) Author: Mark Dickinson (marketdickinson) Date: 2008-06-21 21:11
If n is a Python long, then one might expect float(n) to return the 
closest float to n.  Currently it doesn't do this.  For example (with 
Python 2.6, on OS X 10.5.2/Intel):

>>> n = 295147905179352891391L

The closest float to n is equal to n+1.  But float(n) returns the 
further of the two floats bracketing n, equal to n-65535:

>>> float(n)
2.9514790517935283e+20
>>> long(float(n))
295147905179352825856L
>>> n - long(float(n))
65535L

It's fairly straightforward to fix PyLong_AsDouble to return the closest 
double to a given long integer n (using the round-half-to-even rule in 
the case of a tie).  The attached patch does this.

Having a correctly rounded float(n) can be useful for testing other 
floating-point routines that are supposed to be correctly rounded.
msg71997 (view) Author: David Jones (drj) Date: 2008-08-26 21:10
I agree, longs should be correctly rounded when coerced to floats.

There is an ugly (but amusing) workaround while people wait for this 
patch:  Go via a string:

int(float(repr(295147905179352891391)[:-1]))

Though I assume this relies on the platform's strtod working correctly.  
Which it does for me.
msg75510 (view) Author: STINNER Victor (haypo) Date: 2008-11-04 23:17
You may use "if (nbits == (size_t)-1 && PyErr_Occurred())" to check 
_PyLong_NumBits() error (overflow). Well, "if (numbits > DBL_MAX_EXP)" 
should already catch overflow, but I prefer explicit test to check the 
error case.

Anyway, interresting patch! Python3 vanilla:
>>> n = 295147905179352891391; int(float(n)) - n
-65535

Python3 + your patch:
>>> int(float(n)) - n
1
msg75568 (view) Author: Alexander Belopolsky (belopolsky) Date: 2008-11-06 17:30
Mark,

I noticed that you replaced a call to _PyLong_AsScaledDouble with your 
round to nearest algorithm.  I wonder if _PyLong_AsScaledDouble itself 
would benefit from your change.  Currently it is used in PyLong_AsDouble 
and long_true_divide.  I would think that long_true_divide would be more 
accurate if longs were rounded to the nearest float.

I also wonder whether round to nearest float can be implemented without 
floating point arithmetics.  I would think round towards zero should be 
a simple matter of extracting an appropriate number of bits from the 
long and round to nearest would at most require a long addition.

I believe _PyLong_AsScaledDouble is written the way it is to support 
non-IEEE floating formats, but I am not sure that your algorithm would 
always return the nearest float on an arbitrary non-IEEE platform.

Maybe it would be worthwhile to provide a simple IEEE specific code with   
well specified semantics for both PyLong_AsDouble and long_true_divide, 
but fall back to the current code on non-IEEE platforms.
History
Date User Action Args
2008-11-06 17:30:34belopolskysetnosy: + belopolsky
messages: + msg75568
2008-11-04 23:17:58hayposetmessages: + msg75510
stage: patch review
2008-11-04 23:07:09hayposetnosy: + haypo
2008-08-26 21:10:32drjsetnosy: + drj
messages: + msg71997
2008-06-21 21:11:59marketdickinsoncreate