Mark Dickinson <dicki...@gmail.com> added the comment:

> 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.

----------

_______________________________________
Python tracker <rep...@bugs.python.org>
<https://bugs.python.org/issue32968>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to