Issue47121

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 **2022-03-25 17:28** by **tfish2**, last changed **2022-04-11 14:59** by **admin**.

Messages (8) | |||
---|---|---|---|

msg416010 - (view) | Author: Thomas Fischbacher (tfish2) | Date: 2022-03-25 17:28 | |

>>> help(math.isfinite) isfinite(x, /) Return True if x is neither an infinity nor a NaN, and False otherwise. So, one would expect the following expression to return `True` or `False`. We instead observe: >>> math.isfinite(10**1000) Traceback (most recent call last): File "<stdin>", line 1, in <module> OverflowError: int too large to convert to float (There likewise is a corresponding issue with other, similar, functions). This especially hurts since PEP-484 states that having a Sequence[float] `xs` does not allow us to infer that `all(issubclass(type(x), float) for x in xs)` actually holds - since a PEP-484 "float" actually does also include "int" (and still, issubclass(int, float) == False). Now, strictly speaking, `help(math)` states that DESCRIPTION This module provides access to the mathematical functions defined by the C standard. ...but according to "man 3 isfinite", the math.h "isfinite" is a macro and not a function - and the man page does not show type information for that reason. |
|||

msg416011 - (view) | Author: Raymond Hettinger (rhettinger) * | Date: 2022-03-25 17:52 | |

The math.isfinite() docs could be changed to something like, "coerces x to a float if possible and then returns True if x is neither an infinity nor a NaN, and False otherwise." Or there could be a general note about which functions (most of them) coerce to float (which can fail). With respect to typing and PEP-484, I don't see a bug or documentation issue. Types relationships are useful for verifying which methods are available, but they don't make promises about the range of valid values. For example math.sqrt(float) -> float promises which types are acceptable but doesn't promise that negative inputs won't raise an exception. Likewise, "n: int=10; len(range(n))" is type correct but will raise an OverflowError for "n = 10**100". |
|||

msg416018 - (view) | Author: Thomas Fischbacher (tfish2) | Date: 2022-03-25 21:57 | |

The problem with PEP-484 is that if one wants to use static type analysis, neither of these options are good: - Use static annotations on functions, and additionally spec out expectations in docstrings. Do note that the two types places where "float" is mentioned here refer to different concepts. This looks as if there were duplication, but there actually isn't, since the claims are different. This is confusing as hell. def foo(x: float) -> float: """Foos the barbaz Args: x: float, the foobar Returns: float, the foofoo""" The floats in the docstring give me a guarantee: "If I feed in a float, I am guaranteed to receive back a float". The floats in the static type annotation merely say "yeah, can be float or int, and I'd call it ok in these cases" - that's a very different statement. - Just go with static annotations, drop mention of types from docstrings, and accept that we lose the ability to stringently reason about the behavior of code. With respect to this latter option, I think we can wait for "losing the ability to stringently reason about the behavior of code" to cause major security headaches. That's basically opening up the door to many problems at the level of "I can crash the webserver by requesting the url http://lpt1". |
|||

msg416040 - (view) | Author: Serhiy Storchaka (serhiy.storchaka) * | Date: 2022-03-26 05:55 | |

Most (but not all) functions in the math module implicitly convert its arguments to float. Here we can get an OverflowError. Do we want to add a note to every function that does it? Or add a general note at the top of the file and add exclusion notes to functions which do not convert arguments to float? BTW, there is a general note: "Except when explicitly noted otherwise, all return values are floats." |
|||

msg416840 - (view) | Author: Steven D'Aprano (steven.daprano) * | Date: 2022-04-06 02:51 | |

Isn't this just a quality of implementation issue? math.isfinite should return True for all ints, since all ints are finite. (There are no int infinities or NANs). There is no need to coerce them to float to know that they are finite. Likewise for Fractions. If they overflow, that could be caught and True returned. Decimal infinities convert to float infinities, so the only way you can get an overflow error is if the Decimal is finite but too big to convert. So again, isfinite could (should?) catch the overflow error and return True. Any numeric type that has an infinity which does not coerce to float infinity, but overflows instead, is buggy, and its not isfinite's responsibility to protect against that. So here is my suggestion: isfinite() should return True for all ints, without needing to coerce them to float. For other numeric types, it should try to coerce them to float, and catch OverflowError and raise True. This should be documented, so that other numeric types know what contract they are required to follow (infinity coerces to float infinity). I'm going to change this to an enhancement for 3.11 (or 3.12). |
|||

msg416842 - (view) | Author: Raymond Hettinger (rhettinger) * | Date: 2022-04-06 03:58 | |

> isfinite() should return True for all ints, without needing > to coerce them to float Whoa there. You're venturing into changing what those math functions were all about and the core approach to how they operate. A zigzag to this new direction would need discussion with all the math folks. Many tools in the math module are thin wrapper around libmath and we never intended to be universal handlers of all numeric types. We have a lot of functions that start by coercing their input to a float. My two cents is that this would open an endless can of worms and make everyone pay a time and complexity cost for a feature that almost no one needs. AFAICT is more of a purity issue than an actual practical need that would warrant separate code paths (for example, we recently closed an issue where someone wanted math.log() to have a separate code path for Fraction inputs where it return log(f.numerator) - log(f.denominator) with the goal of handling fractional values smaller than float_min). |
|||

msg416843 - (view) | Author: Tim Peters (tim.peters) * | Date: 2022-04-06 04:13 | |

I'll testify that I won't volunteer one second of my time pursuing these abstract "purity" crusades ;-) `isfinite()` et alia were added to supply functions defined by current standards to work on IEEE floating-point values. People working with floats have reasonable expectations that Python will supply workalikes for industry-stand float functions. If was had to cater to all possible "numberish" types, we'd never add anything new again :-( Doing that in a principled way requires dedicated new dunder methods, and that's a high bar. That I said, I extended math.log() decades ago to work on giant integers. It was useful for real projects I was working on at the time - I was scratching my own itches. |
|||

msg416969 - (view) | Author: Thomas Fischbacher (tfish2) | Date: 2022-04-08 10:09 | |

Tim, the problem may well be simply due to the documentation of math.isfinite() being off here. This is what we currently have: https://docs.python.org/3/library/math.html#math.isfinite === math.isfinite(x) Return True if x is neither an infinity nor a NaN, and False otherwise. (Note that 0.0 is considered finite.) New in version 3.2. === If this were re-worded as follows (and corresponding changes were made to other such functions), everyone would know what the expectations and behavior are: === math.isfinite(x) If `x` is a `float` instance, this evaluates to `True` if `x` is neither a float infinity nor a NaN, and `False` otherwise. If `x` is not a `float` instance, this is evaluates to `math.isfinite(float(x))`. New in version 3.2. === This would be an accurate defining description of the actual behavior. Note that, "thanks to PEP-484", this abbreviation would currently be ambiguous though: === math.isfinite(x) If `x` is a float, this evaluates to `True` if `x` is neither a float infinity nor a NaN, and `False` otherwise. If `x` is not a float, this is evaluates to `math.isfinite(float(x))`. New in version 3.2. === ("ambiguous" since "float" means different things as a static type and as a numbers class - and it is not clear what would be referred to here). Changing/generalizing the behavior might potentially be an interesting other proposal, but I would argue that then one would want to change the behavior of quite a few other functions here as well, and all this should then perhaps go into some other `xmath` (or so) module - bit like it is with `cmath`. However, since the Python philosophy is to not rely on bureaucracy to enforce contracts (as C++, Java, etc. do it), but instead to rely on people's ability to define their own contracts, making the math.isfinite() contract more accurate w.r.t. actual behavior in the CPython implementation via extra clarification looks like a good thing to do, no? |

History | |||
---|---|---|---|

Date | User | Action | Args |

2022-04-11 14:59:57 | admin | set | github: 91277 |

2022-04-08 10:09:13 | tfish2 | set | messages: + msg416969 |

2022-04-06 04:13:57 | tim.peters | set | messages: + msg416843 |

2022-04-06 03:58:49 | rhettinger | set | nosy:
+ tim.peters, mark.dickinson messages: + msg416842 |

2022-04-06 02:51:00 | steven.daprano | set | versions:
+ Python 3.11 nosy: + steven.daprano messages: + msg416840 type: enhancement |

2022-03-26 05:55:57 | serhiy.storchaka | set | nosy:
+ serhiy.storchaka messages: + msg416040 |

2022-03-25 21:57:44 | tfish2 | set | messages: + msg416018 |

2022-03-25 17:52:35 | rhettinger | set | nosy:
+ rhettinger, docs@python messages: + msg416011 assignee: docs@python components: + Documentation |

2022-03-25 17:29:44 | Nathaniel Manista | set | nosy:
+ Nathaniel Manista |

2022-03-25 17:28:43 | tfish2 | create |