-
-
Notifications
You must be signed in to change notification settings - Fork 30.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Expose PyFloat_AsDouble at Python level: operator.as_float? #84978
Comments
Motivation Various pieces of Python need to do a duck-typed conversion of an arbitrary float-like object to a float (or a C double) for computation. The math module is the most obvious example - most math-module functions that accept a float also accept float-like things, like np.float32 and Decimal - but it's not the only place that this is needed. This conversion is easy at C level, being encapsulated in a single function call: PyFloat_AsDouble. (Plus a PyFloat_FromDouble if you want to go back to Python space, of course.) But: it's surprisingly awkward to get an equivalent effect in pure Python code. Options are:
This has bitten me in Real Code (TM), where I've needed a way to convert an arbitrary float-like thing to a float, ideally following the same rules that Python uses. And also ideally in such a way that my own code doesn't have to change if Python updates its rules, as for example it did recently to allow things with an __index__ to be considered float-like. Proposal Add a new operator function "operator.as_float" which matches Python's duck-typed acceptance of float-like things, in the same way that the existing operator.index matches Python's implicit acceptance of int-like things in various APIs that expect integers. Internally, "operator.as_float" would simply call PyFloat_AsDouble followed by PyFloat_FromDouble (possibly with a fast path to pass objects of exact type float through directly). Related: bpo-17576. |
Proof of concept in #64680 |
class float:
@classmethod
def from_floatlike(cls, obj):
return cls(PyFloat_FromDouble(PyFloat_AsDouble(obj))) which would work to get an instance of any float subclass after a round-trip through a double. I have no idea whether that's actually useful, though :) |
Yes, it's not ideal. It's by analogy with operator.index, which provides the equivalent duck-typing for integers, and calls __index__. Similarly, operator.as_float is primarily there to call __float__, except that now that PyFloat_AsDouble also makes use of __index__, it'll call that, too. My other thought was putting this in math, since it's what the math module is already doing implicitly to most inputs. An alternative float constructor could work. Though we then run into the messy question of what it should do for float subclasses ... |
On naming, maybe float.from_number or float.from_numeric? |
I think an alternative constructor is the best option. Some time ago I proposed to add more alternative constructors: https://mail.python.org/archives/list/python-ideas@python.org/thread/5JKQMIC6EUVCD7IBWMRHY7DRTTNSBOWG/ |
The problem with the roundtrip PyFloat_FromDouble(PyFloat_AsDouble(obj)) is that (in contrary to PyNumber_Index()) it is lossy. Converting Decimal, Fraction, float128 to float before using it in expression can lead to loss of precision. So such conversion looks to me less useful than operator.index(). |
That may be true, but it's still useful, and currently very hard to write without core Python support. The problem is that duck-typing for something float-like is easy and widespread in the Python C code (in the math module, in float formatting, in *any* function that uses the "d" converter with PyArg_ParseTuple), but it's hard to correctly spell the equivalent in pure Python. I've needed this in a few places. Most recently, I needed it for the Enthought Traits library: the "Float" trait type accepts something float-like and needs to convert it to something of exact Python type float. (That float is then often consumed by other third-party libraries that can't reliably be expected to do their own duck-typing.) It would also be useful when trying to create pure Python equivalents for modules written in C (e.g., for things like datetime and decimal). Where the C code uses PyFloat_AsDouble, there's really no equivalent that can be used in Python. |
My experience is that this loss of precision is hardly ever a practical problem in the real world of scientific development; in practice floating-point numbers are almost universally IEEE 754 doubles (perhaps sometimes single-precision in large datasets, like seismic SEG-Y files; occasionally IBM format hex floats; but IEEE 754 doubles are by far the majority). It's very rare to be using float128 or Decimal or Fraction in practice for scientific data. That's not to say that people outside that world won't be using these things, but there's a big ecosystem where float64 is pretty much all you need. |
I started a python-ideas thread: https://mail.python.org/archives/list/python-ideas@python.org/thread/3YGNHGWZOU5AIBS3A52CAHPJJLY7J2CS/ |
PR 26827 is a draft for float.from_number() and complex.from_number(). |
Since 3.7 it was deprecated (#71170) and maybe it's time to remove this (#109311). So, it's not an issue in that part.
Then why not export Perhaps, a different constructor could be more discoverable option. But I don't see why we can't expose mentioned above dunder methods just as for the |
They are alternate constructors which only accept numbers (including objects with special methods __float__, __complex__ and __index__), but not strings.
…thonGH-26827) They are alternate constructors which only accept numbers (including objects with special methods __float__, __complex__ and __index__), but not strings.
@serhiy-storchaka, probably this can be closed? |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
Linked PRs
The text was updated successfully, but these errors were encountered: