-
-
Notifications
You must be signed in to change notification settings - Fork 31.4k
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
numbers.Rational implements __float__ incorrectly #68163
Comments
numbers.Rational defines __float__ like this: def __float__(self):
"""float(self) = self.numerator / self.denominator
This assumes that division of two Integral numbers returns a float, which the numbers ABC does not enforce in any way. return float(self.numerator / self.denominator) |
What happens if |
Good point. If the numbers ABC guaranteed numerator and denominator to be Integral numbers, this could be solved by: return float(int(self.numerator) / int(self.denominator)) but since both could be Rationals again that does not seem to be an option either. num = self.numerator
den = self.denominator
while not (isinstance(num, Integral) and isinstance(den, Integral)):
num = num.numerator * den.denominator
den = den.numerator * num.denominator
return float(int(num) / int(den)) Clearly that's more complicated, but, more importantly, has the disadvantage that the loop will run forever if the final numerator or denominator is not registered correctly in the numeric tower. |
Is it not reasonable to simply say that implementations of numbers.Rational which allow the numerator and denominator to have types for which true division doesn't return a float, have to provide their own implementation of __float__()? It's certainly less convenient, and probably surprising for users, but the alternative is trying to work around broken integer types - after all numbers.Complex.__truediv__ says "Should promote to float when necessary" in the docstring, which to me says that a type where a/b doesn't return a float doesn't conform to the numeric tower. |
Alternatively, return int(self.numerator) / int(self.denominator). After all, a fraction whose numerator can't be represented as a Python (unlimited precision) integer is a pretty odd sort of fraction... |
Unfortunately, the Rational type cannot always know what its numerator or denominator supports.
I do read this docstring differently. To me, it means should promote to float if there is no other way to express the result (like when your custom type system does not define its own Float type).
The problem here is that self.numerator and/or self.denominator could both be Rational instances again, which are not expressible as a Python integer and, thus, are not enforced to provide __int__ by the numbers ABC. |
After considering this for a while, I think: return float(self.numerator / self.denominator) is the best solution:
I've attached a tentative patch. |
…thonGH-25619) * pythongh-68163: Correct conversion of Rational instances to float Also document that numerator/denominator properties are instances of Integral. Co-authored-by: Mark Dickinson <dickinsm@gmail.com> (cherry picked from commit 8464b75) Co-authored-by: Sergey B Kirpichev <skirpichev@gmail.com>
…thonGH-25619) * pythongh-68163: Correct conversion of Rational instances to float Also document that numerator/denominator properties are instances of Integral. Co-authored-by: Mark Dickinson <dickinsm@gmail.com> (cherry picked from commit 8464b75) Co-authored-by: Sergey B Kirpichev <skirpichev@gmail.com>
Thanks, looks like this has been completed |
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:
The text was updated successfully, but these errors were encountered: