Author mark.dickinson
Recipients elias, jyasskin, mark.dickinson, rhettinger, serhiy.storchaka
Date 2018-03-19.18:09:57
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1521482997.3.0.467229070634.issue32968@psf.upfronthosting.co.za>
In-reply-to
Content
> Mark, I tried `Fraction(10**23) // 1e22`, and I got 10.

Sorry: that result was with your PR (as it was at the time I wrote that comment). On master, you do indeed get 10.

> I think the fact that floating-point rounding error sometimes causes
> strange results is not a reason to do really unexpected things like
> making 1.0 // 1/10 equal 9.0, if that can be reasonably avoided.

I understand, but I think in this case, the cure is worse than the disease. I don't think converting the float to a Fraction in mixed-type operations is going to work, for a number of reasons:

- it's changing the behaviour for _all_ the arithmetic operations, not just // and %; that widens the scope for accidental breakage. For example, with the latest commit 038664e6a6288f6113ab96103e57d3b25b39a8c2 on your PR:

>>> Fraction(1) + math.inf
inf
>>> math.inf + Fraction(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/mdickinson/Python/cpython/Lib/fractions.py", line 391, in reverse
    return float(fallback_operator(Fraction(a), b))
  File "/Users/mdickinson/Python/cpython/Lib/fractions.py", line 130, in __new__
    self._numerator, self._denominator = numerator.as_integer_ratio()
OverflowError: cannot convert Infinity to integer ratio

But on master, we have:

>>> import math
>>> from fractions import Fraction
>>> math.inf + Fraction(1)
inf
>>> Fraction(1) + math.inf
inf

- it's counter to the spirit of the numeric tower, where for most arithmetic operations, values are propagated _up_ the tower (from Integral -> Rational -> Real -> Complex) to the first common type before the operation is performed. That keeps things simple; having some cases where values are converted down the tower is going to cause confusion. (Comparisons are necessarily a special case: the need to preserve transitivity of equality and trichotomy means that using the same rules as for arithmetic operations won't fly)

- it introduces a non-obvious difference between mixed-mode Fraction-float and int-float operations. For an int x and a float y, I'd expect the result of `x <some_op> y` to be identical to the result of `Fraction(x) <some_op> y`.

So I'm sorry, but I do think having 1.0 // Fraction(1, 10) give 9.0 rather than 10.0 is the least worst option here. It's a surprise, but it's not a _new_ surprise: it's a surprise that's already there in the world of floating-point arithmetic.

>>> 1.0 // 0.1
9.0

You're evaluating a discontinuous function _at_ a discontinuity, using a type that's known for its inexactness. In that situation, getting the value for _either_ side of that discontinuity is reasonable.
History
Date User Action Args
2018-03-19 18:09:57mark.dickinsonsetrecipients: + mark.dickinson, rhettinger, jyasskin, elias, serhiy.storchaka
2018-03-19 18:09:57mark.dickinsonsetmessageid: <1521482997.3.0.467229070634.issue32968@psf.upfronthosting.co.za>
2018-03-19 18:09:57mark.dickinsonlinkissue32968 messages
2018-03-19 18:09:57mark.dickinsoncreate