It has everything to do with this discussion. You are saying that floordiv should return an integer to match math.floor
, and I was saying that floordiv is the correct one between the two.
Is this possible if subnormals are disabled? Or does that not count as an IEEE-754 system? (Given that we accept other anomalies relating to signalling and quiet NaN, it might count.)
Though Iâm not sure of any way to disable subnormals for testing, so that may be moot anyway.
Agreed. Indeed, under the rules of PEP 387 I believe this would be a necessary condition for making the proposed change. (But by no means sufficient. Finding at least one core developer who supported the change would also be helpful, for example.)
Also agreed. That is, I can see possible mechanisms for this, but nothing whose complexity doesnât vastly outweigh the potential benefits.
I believe that leaves you at step 6 of the âMaking Incompatible Changesâ section of PEP 387:
- If a warning cannot be provided to users, consult with the steering council.
Note: âshould we have done this differently in the first place?â is a very different question from âshould we change the established behaviour now?â. Iâm not 100% sure what the answer to the first question is, but itâs academic if the answer to the second is a loud clear âNo!â.
Yep. See python - Possible to have ``a < b and not(a - b < 0)`` with floats - Stack Overflow for example, horribly hacky, platform-specific code.
FWIW, it seems to me that @ntessore is actually making a similar point to you in advancing that math.floor
, .ceil
, etc. should in fact return float
s rather than int
s for float
inputs, and by extension that floor division is indeed correct in returning a float (as you also support).
This would almost certainly need a PEP, and thus convincing at least one core dev or PEP editor would also be a necessary (but also far from sufficient) requirement to move this forward, as such would be needed to sponsor said PEP. FWIW, as one, I donât have strong feelings on this but am not certainly convinced the breakage, which seems quite difficult to warn about, would be worth the claimed benefits (assuming for the benefit of the doubt that they are indeed present).
Iâll note that in PEP-3141, the docstring of the provided code does imply that __floordiv__
return integers the same as __floor__
and __ceil__
and __round__
-with-a-single-argument. See the Real class, which is the ABC equivalent of float, all four methods use the same âIntegralâ wording.
The actual recommendation section doesnât mention // and __floordiv__
, so itâs understandable that it was not implemented at the time (and it also recommended floor, ceil and round to keep returning floats but only for 2.6, otherwise the word used was Integral, the same as the __floordiv__
docstring). This looks more and more like an oversight.
My proposition would then be a sort of continuation of 3141, finishing it and making it consistent (I donât know if it changes anythong about requiring another PEP), while their (sorry, @steven.daprano and @ntessore) proposition is a revert of 3141âs entire stance on floor, floordiv, round and ceil. Which theyâre certainly allowed to introduce, but itâs undoubtedly more consequential, and in reverse of the direction Python has taken since 15 years ago (if I believe the date on 3141).
Division of float
currently throws ZeroDivisionError
for division by zero.
In numpy
division of numpy.float64
gives a runtime warning for division by zero.
It looks like the philosophy in Python is to be vocal about the division by zero. Seems consistent for //
to be vocal about division by zero too.
This is not an impediment to obtaining float('inf')
or float('nan')
as result, since one can either use numpy.float64
or catch ZeroDivisionError
and return the corresponding value.
Okay, Iâm gonna dismiss that as âyou can break ANYTHING if you use ctypesâ Good to know.
I just did the same search (should have read the rest of this thread first!) If nothing else, a doc PR would be good. Maybe to:
In fact, it would be good to document what all the binary arithmetic operations do with regard to type. And putting it in that context does make that point that I think they all do the same thing with mixed types. So while inconsistent with floor, the current behaviour is consistent with the rest of the arithmetic operators.
As an academic interest I think itâs worth talking about â but another academic question is whether it was done this way deliberately or as an oversight. As noted above â It very well could have been a deliberate decision to keep
//
consistent with the other arithmetic operators â or deliberate to keep it consistent with what the py2 __future__
import did.
Whatâs the use case is for float.hex
? If itâs useful, why isnât there an int.hex
?
Getting the raw IEE 754 binary value of a float
as a str
, which could conceivably be useful for introspection, raw manipulation and interoperability, serialization, communication, etc.
Well, thereâs the hex()
builtin, but the semantics are differentâit is simply the integer number represented in hex, as opposed to the hex of the bits in the underlying implementation. And fundamentally, the hex value of the underlying bytes of an integer are of course not compatible with those of a float
in either directionâinterpreting one as the other would result in, to borrow a word from another commentor, ânonsenseâ. This is too low a level of implementation details for the abstraction of int
being a subtype of float
to be type safe.
In numpy
division of numpy.float64
gives a runtime warning for division by zero.
[/quote]
Note that the primary reason that numpy doesnât raise (by default) is that itâs âvectorizedâ â if you have one zero in a million element array, you really donât want the computation to stop dead. Rather, you can check for finite values at the end of the entire computation.
I think the issue at hand is that: the IEEE special values propogate through //
:
In [21]: math.nan // 2
Out[21]: nan
but they would have to raise if //`` returned an int, as there are no int equivalents for
Inf,
NaN`, and friends.
Which brings up a thought â now that ints are âlong intsâ, they are not directly tied to the underlying long int
C data type â so maybe it could have inf
and NaN
values ⌠This would be distinct from all other (most other?) languages, so may not be worth it, but it would close one gap in the numeric tower.
A lot of code, including stdlib code, is relying on int only having actual-number values. Doing this change would raise a lot of new questions : what is range
supposed to do when passed a Nan ? And slicing ? It also makes a lot of operations âunsafeâ - meaning they can return a nan when they previously couldnât - and the code using it as well.
Personnaly, I wouldnât be sold about adding inf, nan to float
itself if it were on the table - which it is not - so Iâm just as unconvinced about adding those to int.
Except if it can make my proposition accepted and add my name to Pythonâs credits of course
More seriously though, what about the solution where // returns either an int (or a ZeroDivisionError), or a float but just in the case of inf and nan ?
It would make the operator returning different types besed upon the value of its parameters, which sounds weird even to me (Iâm not convinced by this very idea, I just think itâs worth mentioning), but if those three values (-inf, nan and +inf) are special enough to be the only ones currently returned by //
on which .is_integer()
is not True, and if they are special enough to warrant an exception in math.ceil, math.floor and round (and in // in my opinion), maybe they are special enough to break the single-return-type rule
Backwards compatibility is always important, but Iâm not sure this would be so bad â passing a non-finite value would raise an exception just like passing a None
would now. But now that I think about it more, one could certainly argue that there is no need for special values for int
, as you can always use sentinels that are another type.
Python didnât originally have proper support for the IEEE special values, perhaps for this very reason. It was added on later (a bit piecemeal) I think because interaction with non-python libs (e.g. numpy) makes it unavoidable.
So perhaps operations on floats could return special values rather than raise:
math.nan // 5.0 is None
But that would be a divergence from Pythonâs current behavior, e.g. ZeroDivision Error. It might make some sense though now that we have comprehension and a LOT of iteration-based programming.
In [24]: [5.0 / x for x in (1,2,3,4,5,6,0,5,2)]
---------------------------------------------------------------------------
<ipython-input-24-c82bc0c1bc22> in <listcomp>(.0)
----> 1 [5.0 / x for x in (1,2,3,4,5,6,0,5,2)]
2
ZeroDivisionError: float division by zero
Itâs really a pain to have the entire comprehension fail due to one value, particularly if itâs deep in a nested set of iteratablesâŚ
Having float.__div__
raise ZeroDivisionError
, and using your own function that handles the exception instead of 5.0 / x
when you intend to handle division by zero, seems adherent to the Zen of âExplicit is better than implicit.â in my opinion.
The opposite alternative would have you chasing your deep nesting of iterations for which computation originated a non-finite float, when and if you discover at the end of the computation that there is a non-finite float in the results.
I started writing something in the form of a PEP, in case this is deemed to need one. PEP Integral Division - HedgeDoc
There are some missing points here and there, and some minor things not discussed here.
I also realized doing this may open the door to a new feature : a//b
returning an accurate integer value greater than the largest float. for example, int(sys.float_info.max)//.5
is int(sys.float_info.max)*2
, and that resolves to a valid integer value. Exotic implementation could detect and support division by inverses of integers (and theoretically any number smaller than 1, even though I donât know how we would implement that). While I donât think thatâs a priority at all, itâs interesting to know that this could become feasible.
The PEP and this thread assume that the purpose of //
is to obtain an integer, and see this in isolation.
I do not know what is the intension, but here is an aspect of the role of //
that could suggest otherwise.
In the integers (here not referring to int
but to \mathbb{Z}) we have Euclidean division. Namely,
given a\in\mathbb{Z} and b\in\mathbb{Z} with b\neq0, there exists a unique pair q,r\in\mathbb{Z} such that
We have the same result replacing \mathbb{Z} with \mathbb{R}, real numbers.
Coming back to Python, for a
and b
instances of int
or instances of float
we can compute q
and r
using
q = a // b
r = a % b
The exception ZeroDivisionError
stopping us from falling in the case b == 0
. There is a difference in Python that a % b
doesnât always give a non-negative r
.
Whether or not we have a == b*q + r
, I havenât thought about it, but at least we can test it for all the values for which a // b
and a % b
can be computed.
If //
returns an int
, then it is possible to have a
and b
such that we cannot compute b * q + r
. in other words
(a // b) * b + (a % b)
For example, if a
is a large int
or float
and b
a small positive float
such that a // b
is an int
that would give OverflowError
when converted to float
in the multiplication (a // b) * b
.
What I describe in the PEP allows, indeed, for implementations to make ints larger than the largest float come out of an integer division. But Iâm not saying itâs now required. I think itâs opening the door to that feature and itâs an interesting one, but if you prefer I can move it out of the recommendation section and in an âopen-endâ section as a regard to the possible future evolutions this enables ?
I donât think what you say is enough to close the door on generating bigger-than-float ints rather than raising an OverflowError, but at the same time Iâm not convinced it definitely should be on the list of things to do and I also think itâs only tangentially related to this question of the return type.
But I certainly feel what youâre saying : I tried making an implementation of the algorithm I drafted near the end, and I simply canât check the results because then everything overflows all the time !
So, I think Iâll revert the recommendation back to âit should raise OverflowError whenever it used toâ.
One other thing to keep in mind that youâll need to submit a draft PEP besides a sponsor and at least rough consensus here that a PEP is appropriate (even if not agreement on the specifics of the proposal), is youâll want to have a reference implementation, usually in the form of a PR to the cpython
repo or your fork of it, at least as a prototype. Sometimes PEPs are initially published to the PEPs repo with a good reason, or if the author commits to providing one prior to acceptance and we have a basis to trust that, but in general especially in this case IMO it would be best to have at least a rough working prototype prior to merging (and ideally, submitting) a formal draft PEP.