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
Floor divide should return int #66634
Comments
PEP-3141 defines floor division as floor(x/y) and specifies that floor() should return int type. Builtin float type has been made part of the PEP-3141 numerical tower, but floor division of two floats still results in a float. See also:
|
Is this change compelling enough to break compatibility, or is it just a matter of purity? |
Perhaps it's worth mentioning that several people on Python-ideas took the PEP-3141 does not mention Infinities and NaNs: "The Real ABC indicates that the value is on the real line, and supports Floats, however, are on the extended real number line, so we have a problem. :) Other languages The PEP says that inspiration came from Scheme and Haskell. However, Scheme returns floats: https://mail.python.org/pipermail/python-ideas/2014-September/029432.html Haskell seems to return the highest representable integer: Prelude> floor (1/0) However, Haskell float support looks sketchy: Prelude> floor (0/0) Prelude> let x = 1 / 0 Considering the last two examples, I think Haskell should not provide any |
Argh, forget the second Haskell example: inf / 0 is fine. |
According to the PEP-3141, Integer is a subtype of Real, so one should be able to substitute an Integer whenever Real is expected. The reverse is not true, for example >>> [1,2][1.0]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: list indices must be integers, not float This is one of the common uses of floor division - to find an index of a cell in a regular grid: (x - start) // step. In this situation, it is convenient to have the result ready to be used as an index without a cast. The substitution principle also suggests that compatibility issues are likely to be small: in most contexts integers behave the same as floats or "better". Here is an example of a "better" behavior: >>> x = 1 + 10**50
>>> x * 1 == x
True
>>> x * 1.0 == x
False The only case I can think of where float result may be preferable is inf // 1 because integers cannot represent infinity. However, this case is arguably already broken. What are the use-cases for float // float where integer result is not acceptable? |
Does Scheme's default integer type support arbitrarily large values? |
Yes, at least is does on the version I tested. |
I sympathize with the idea that math module functions should return floats. I find it unfortunate that math.floor delegates to the __floor__ dunder on non-floats instead of doing math.floor(x.__float__()). It would be more natural to have a floor builtin that *always* delegates to __floor__ and keep math a pure float library. Note that math module provides the means to compute C-style floor: >>> x = float('inf')
>>> math.modf(x)[1]
inf
>>> x = -3.4
>>> math.modf(x)[1]
-3.0 Maybe we should add floorf, ceilf, etc. as well. This, however, is a different issue from the one at hand here. |
It can lead to unexpected memory consumption when dealing with I'm also concerned that returning a very large integer will lead users
|
I think Decimal case should be considered separately. Note that unlike float, they are not part of the numerical tower, so PEP-3141 arguments don't apply: >>> isinstance(1.0, numbers.Real)
True
>>> isinstance(decimal.Decimal(1), numbers.Real)
False |
On Sat, Sep 20, 2014 at 9:38 AM, Alexander Belopolsky
+1
That's not immediately obvious...
i think the issues are related. PEP-3141 defines x//y as I maintain gmpy2 which wraps the GMP, MPFR, and MPC arbitrary My preference would be to define floor division and divmod in terms of I mentioned my concerns about memory growth in another comment. I'm |
>> What should Decimal('1e123456')//1 return?
>
> I think Decimal case should be considered separately. Note that unlike float, they are not part of the numerical tower, so PEP 3141 arguments don't apply:
>
>>>> isinstance(1.0, numbers.Real)
> True
>>>> isinstance(decimal.Decimal(1), numbers.Real)
> False
>
I maintain gmpy2 and I've had requests to support the numeric tower.
gmpy2 has integral, rational, real, and complex types so I should be
able to. |
I can't think of any. I was mostly making the case for conservatism here. The indexing use case is interesting, although I suppose enumerate() should eliminate most instances of it. |
I agree with Antoine that making this change is a really bad idea.
# Here is a simple example of a chain of calculations from __future__ import print_function
from fractions import Fraction
from decimal import Decimal
def f(x, y):
return x // 3 * 5 / 7 + y
def g(x, y):
return int(x // 3) * 5 / 7 + y for x, y in [ In Python 2: In Python 3: 3.5238095238095237 3.5238095238095237
Traceback (most recent call last):
...
return int(x // 3) * 5 / 7 + y
TypeError: unsupported operand type(s) for +: 'float' and 'decimal.Decimal' I am a strong -1 against breaking code that relies on the floor division being type preserving. The PEP should be revised to say that floor division is defined to return a value that is *equal* to an Integral but not place any restriction on the return type. |
Floor division on floats is an unattractive nuisance and should be removed, period - so there ;-) But short of that, I favor leaving it alone. Whose life would be improved by changing it to return an int? Not mine - and doing so anyway is bound to break existing code. +1 on changing the PEP instead (as Raymond suggested). |
I can't say that I've ever used // on floats, but it seems to me anyone doing so (as opposed to normal division + explicit rounding) *intentionally* might be broken by this change, but anyone doing this incidentally is not really in a "gotcha" situation. Since this is a type-specific behavior, and not a value-specific one, I don't really think there's a win in changing the behavior, and staying backwards compatible is much better. |
[Raymond]
No, in the fractions module floor division returns an int: >>> type(Fraction(2) // Fraction(1))
<class 'int'> It is also implemented in the datetime module where >>> type(timedelta(2) // timedelta(1))
<class 'int'> [Raymond] def f(x, y):
return x // 3 * 5 / 7 + y
def g(x, y):
return int(x // 3) * 5 / 7 + y
[/Raymond] I am not sure what is the problem here. In Python 3: >>> f(12.143, 0.667)
3.5241428571428575
>>> g(12.143, 0.667)
3.5241428571428575 |
-1 from me, too. It's an unnecessary change, and the conversion from float to integer potentially expensive compared to the computation of the floating-point result (especially in extended floating-point implementations that allow a wider exponent range). If this is about consistency between |
[Raymond]
If we take this route, what float('inf') // 1 and float('nan') // 1 should return? |
Probably exactly the same as they do right now. I think there's an argument that |
Mark, Raymond suggested that "The PEP-3141 should be revised to say that floor division is defined to return a value that is *equal* to an Integral". Since nan or inf are not *equal* to any Integral, the current implementation does not comply. In the absence of a recommendation in the PEP, implementers of new numeric types are left with little guidance because existing types are inconsistent: >>> Decimal('inf') // 1
Decimal('Infinity')
>>> float('inf') // 1
nan |
Alexander Belopolsky <report@bugs.python.org> wrote:
I guess it should say "equal to an Integral or a special value".
I think both should return inf. |
skrah> I think both should return inf. What about this case: >>> Decimal('1') // Decimal('-inf')
Decimal('-0')
>>> 1. // float('-inf')
-1.0 |
I think that one's covered by bpo-22198. |
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: