Make float.__(i/r)floordiv__ return an int

Thanks for writing this up as a (proto) PEP – I am a supporter of the idee, but even if it’s rejected, it’s really good to have the discussion and conclusion documented.

Is this thread the best way to comment / suggest changes to the PEP draft (PEP Integral Division - HedgeDoc) ? I haven’t found a way to do that on the publishing platform – NOTE: using gitHub or the like has advantages for others being able to make suggestions to the text… Annyway, I’ll do it here for now.

typing recommendations include considering float -typed parameters as accepting int arguments"

Here’s the citation:

And it’s not just a recommendation, it’s built in to what float means in type hints. (at least as I read that – I’m no typing expert)

The int API adds, in addition to supplementary methods, the support for slicing

You should probably explicitly talk about __index__ here.

float s include decimal values and infinities, and int s include values bigger

floats DO NOT support decimal values – they are specifically binary values (that get rounded to decimal for display, etc) – did you mean fractional values?

And I would separate out precision issues from the float special values. They are pretty distinct concepts. Python ints are pretty unique (or special anyway) by being unlimited precision. and, in theory, Python could introduce an unlimited (or variable anyway) precision float type (similar to Decimal).

Which, while I’m at it should be discussed as well, not built in, but it is part of the stdlib, and currently, an_int // a_decimal returns a Decimal type. (like float, natch).

The a//b operation was initially described, in :pep:??? , as
PEP 238: (PEP 238 – Changing the Division Operator | peps.python.org).

The exact quote is:
“”"
Floor division will be implemented in all the Python numeric types, and will have the semantics of:

a // b == floor(a/b)
“”"

The __floordiv__ dunder was however missing in the 3141 Recommendations section, which this PEP seeks to complete.

I think this is THE key point here – in my mind this is clearly an inconsistency – the question is whether it was intentional or not – I have been able to find no documentation that makes that clear. The only place __floordiv__ is mentioned in PEP 3141 is that it “goes away” for the complex type.

Folks who’ve been around a while (@mdickinson @tim.one @guido, … ) – does anyone recall this being discussed as part of the PEP 3141 process?

return an int object whenever they used to return a float with an integer value (on which value.is_integer() is True).

This is a bit confusing to me – but in any case, I think it should specify that, for mixed types, a//b would return:

int(divmod(a, b)[0])

With the same coercion rules as divmod

However, in theory, the implementation could keep the extra precision available in ints through the computation, but I don’t think there’s any easy (and performant) way to do that now.

It seems we could say a//b is exactly floor(a/b) – however, if the truncation is done after the division, rather than all in “float space”, you will get a different result in some cases – got to love FP!:

In [25]: math.floor(6 / .4)
Out[25]: 15

In [26]: 6 // .4
Out[26]: 14.0

(see: Integer division for dicussion and Tim Peter’s excellent explaination)

I’d personally prefer the floor(a / b) version, but that would change the value of the result in these cases :-(.

At least one version of Python has the float methods return int objects, except that it still returns float("nan"), float("inf") or float("-inf") in cases when it used to and when following this PEP would make it raise an exception.

I found the working here a bit confusing – when I first read it, I thought it means that there was a python implementation (e.g. PyPy or micropython, or…) but I now see it’s an optional transition state. It’s an interesting idea – I suppose it’s a question of when the Exception is raised – when the // operation is performed, or when you try to use the result as an integer (e.g. __index__).

The former may be spotted by type checkers

Side note – you may want to say more about typing and type checkers – it seems that Python is moving toward a more “statically typed” stance – I don’t personally like that, but having // always return an int would sure make things easier for type checkers. I actually have no idea if there is a way to type a function like this one, where the return type depends on the input types – but it’s certainly not straightforward. As it’s a builtin, I imagine the type checkers have special cased it, but for mere mortals, it’s pretty tricky to reason about.

This is most likely a small use-case among all uses of floor-division : calculating an integer value, then involving it in further calculations specifically involving floats

I agree here – in “real code” if you care, you could use divmod() – and in any case, the issue is already there with floor() and round() and ceil()

In my experience, I am FAR more likely to apply // or floor() or ceil() as the last step in a computation – but maybe others have a different experience?

Which brings us back to what I think is the main point of this:

It makes floor division consistent with the PEP 3141 number hierarchy. Some on this thread have argued that that was a mistake – fine, but then the response should not be:

“let’s keep this inconsistency”

but rather

“roll back PEP 3141”

Keeping an inconsistency seems like the worst of both worlds.

Ahh – I see in the section " Ramble about overflow …" you do talk a bit about one point above – maybe it should be included (in some form) e.g. “Possible implementation that maintains higher precision”

Finally: I’m not sure how, or if, to express this in the PEP, but as I followed this entire discussion, it struck me that most of the challenges were essentially all the same problems that already exist with FP – just shifted to a slightly different location in the run-time behaviour of the code. There’s no getting around that if you use floats, you have to deal with float issues. If some code happens to work OK without having thought about it (e.g. what happens when you get a , that may be a bad thing, rather than a good one.

Final note: it may be worth mentioning what other implementations of __floordiv__ might do – e.g. numpy. As numpy is more focused on homogenous types (and efficient calculations) it will probably not implement __floordiv__ returning integers, as it doesn’t do for floor() and friends currently:

In [34]: arr6 = np.array([60., 6.0, .6])

In [35]: arr4 = np.array([4, 0.4, 0.04])

In [36]: arr6 / arr4
Out[36]: array([15., 15., 15.])

In [37]: arr6 // arr4
Out[37]: array([15., 14., 14.])

In [38]: np.floor(arr6 / arr4)
Out[38]: array([15., 15., 15.])

It would of course, be up to numpy what to do, but it may be worth mentioning.

1 Like