This issue tracker has been migrated to GitHub ,
and is currently read-only.
For more information,
see the GitHub FAQs in the Python's Developer Guide.
Created on 2020年05月28日 09:23 by mark.dickinson, last changed 2022年04月11日 14:59 by admin.
| Pull Requests | |||
|---|---|---|---|
| URL | Status | Linked | Edit |
| PR 20481 | closed | mark.dickinson, 2020年05月28日 11:01 | |
| PR 26827 | open | serhiy.storchaka, 2021年06月21日 11:57 | |
| Messages (11) | |||
|---|---|---|---|
| msg370181 - (view) | Author: Mark Dickinson (mark.dickinson) * (Python committer) | Date: 2020年05月28日 09:23 | |
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: 1. Do an explicit type check to exclude str, bytes and bytearray, and then call the float constructor. But the extra type check is ugly and potentially not future-proof. 2. Call type(obj).__float__(obj). But this has several problems: __float__ can return an instance of a float subclass rather than a strict float, and then it's hard to convert to an actual float. And in more recent versions of Python, this no longer matches PyFloat_AsDouble because it doesn't account for objects that provide __index__ but not __float__. And calling dunder methods directly should rarely be the Right Way To Do It. 3. Use the implicit ability of the math module to do this, for example using math.copysign(obj, obj). This works! But it's not a clear expression of the intent. 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: #17576. |
|||
| msg370189 - (view) | Author: Mark Dickinson (mark.dickinson) * (Python committer) | Date: 2020年05月28日 11:03 | |
Proof of concept in GH-20481 |
|||
| msg370233 - (view) | Author: Zachary Ware (zach.ware) * (Python committer) | Date: 2020年05月28日 16:01 | |
`operator` seems a slightly odd place for this. My naive expectation would be that `float(floatlike_obj)` should do what you want, but it seems that's not the case (too permissive of input types?). So then, what about an alternate constructor on the float object, `float.from_floatlike(obj)`? This could be implemented as effectively: 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 :) |
|||
| msg370244 - (view) | Author: Mark Dickinson (mark.dickinson) * (Python committer) | Date: 2020年05月28日 16:44 | |
> `operator` seems a slightly odd place for this. 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 ... |
|||
| msg370247 - (view) | Author: Mark Dickinson (mark.dickinson) * (Python committer) | Date: 2020年05月28日 16:46 | |
On naming, maybe float.from_number or float.from_numeric? |
|||
| msg370253 - (view) | Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) | Date: 2020年05月28日 17:17 | |
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/ |
|||
| msg370255 - (view) | Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) | Date: 2020年05月28日 17:27 | |
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(). |
|||
| msg370258 - (view) | Author: Mark Dickinson (mark.dickinson) * (Python committer) | Date: 2020年05月28日 18:08 | |
> 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. |
|||
| msg370259 - (view) | Author: Mark Dickinson (mark.dickinson) * (Python committer) | Date: 2020年05月28日 18:26 | |
> Converting Decimal, Fraction, float128 to float before using it in expression can lead to loss of precision. 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. |
|||
| msg370286 - (view) | Author: Mark Dickinson (mark.dickinson) * (Python committer) | Date: 2020年05月29日 10:00 | |
I started a python-ideas thread: https://mail.python.org/archives/list/python-ideas@python.org/thread/3YGNHGWZOU5AIBS3A52CAHPJJLY7J2CS/ |
|||
| msg396234 - (view) | Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) | Date: 2021年06月21日 12:00 | |
PR 26827 is a draft for float.from_number() and complex.from_number(). |
|||
| History | |||
|---|---|---|---|
| Date | User | Action | Args |
| 2022年04月11日 14:59:31 | admin | set | github: 84978 |
| 2021年06月21日 12:00:00 | serhiy.storchaka | set | messages:
+ msg396234 versions: + Python 3.11, - Python 3.10 |
| 2021年06月21日 11:57:39 | serhiy.storchaka | set | pull_requests: + pull_request25408 |
| 2020年05月29日 10:00:07 | mark.dickinson | set | messages: + msg370286 |
| 2020年05月28日 18:26:20 | mark.dickinson | set | messages: + msg370259 |
| 2020年05月28日 18:08:42 | mark.dickinson | set | messages: + msg370258 |
| 2020年05月28日 17:27:56 | serhiy.storchaka | set | messages: + msg370255 |
| 2020年05月28日 17:17:44 | serhiy.storchaka | set | title: Expose PyFloat_ToDouble at Python level: operator.as_float? -> Expose PyFloat_AsDouble at Python level: operator.as_float? |
| 2020年05月28日 17:17:29 | serhiy.storchaka | set | messages: + msg370253 |
| 2020年05月28日 16:46:34 | mark.dickinson | set | messages: + msg370247 |
| 2020年05月28日 16:44:03 | mark.dickinson | set | messages: + msg370244 |
| 2020年05月28日 16:01:32 | zach.ware | set | nosy:
+ zach.ware messages: + msg370233 |
| 2020年05月28日 11:03:44 | mark.dickinson | set | messages: + msg370189 |
| 2020年05月28日 11:01:32 | mark.dickinson | set | keywords:
+ patch stage: patch review pull_requests: + pull_request19730 |
| 2020年05月28日 09:23:29 | mark.dickinson | set | title: Expose PyFloat_ToDouble at Python level: operator.to_float? -> Expose PyFloat_ToDouble at Python level: operator.as_float? |
| 2020年05月28日 09:23:06 | mark.dickinson | create | |