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

That is not noise.

That was.

To answer the valid objection, I’ll first note that it’s __truediv__ and not __floatdiv__ as you said or __div__ as I saw earlier in the thread. That was the py2 syntax, obsoleted long before pep 3141.
Where do you get that assertion that “binary operations are always cast to the least precise type” ? Where is it stated as a rule ?

If you’re describing the current behavior of the cpython distrib, no counter-example comes to mind so I’ll assume you’re right. But if it was a solid rule about Python, why would the documentation use only vague and prudent phrasings such as “The resultant value is a whole integer, though the result’s type is not necessarily int.” (ref) ?
Another example : “The numeric arguments are first converted to a common type. (…) Floor division of integers results in an integer; the result is that of mathematical division with the ‘floor’ function applied to the result.” (ref) ? Why wouldn’t the documentation state that rule there, which would be the best place to put it ?

That’s because it’s not a rule. It’s just something that happens to be true in the current builds, and which makes some sense implementation-wise. But if it’s deemed out of place for the operation returning integer values whatever its operands, aka the floor division or “integer division” as nicknamed in the doc, it should not be referred to as something not to break, because there’s no rule to break.

I had decided that I do not care enough to engage any further, but since you quoted me anyway: I deleted that reply because I didn’t put the statement very well – Firstly, I meant __floordiv__() being the binary operation, and secondly, when having multiple floating point types, the result is actually commonly of the widest floating point type.[1] Perhaps it’s better to say that, when inputs of mixed kind (integer, floating) are encountered, they are commonly understood to express real numbers, and cast to an appropriate type. Your findings seem to bear that out, but I accept that maybe it isn’t as fast a rule as I make it out to be.

  1. Specifically, I would generally expect the C standard rules for implicit conversions. ↩︎

1 Like

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 |

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

I added the top recommendations.

By that I meant numbers with points. I can’t say “non-integers” because in context it would be a circular statement, and “real values” seems sketchy for someone not versed into set-based maths. But a more accurate term would be good for sure.

With the implementation being as it stands, there’s not much choice offered to a developper of the Decimal type : it’s closer in purpose and behavior to float than to int, so you calk it on float’s behavior. All in all, it’s the most consistent choice in order to adhere with the inconsistent int//float API.

Yes, though… I also want divmod to return an int, and for the exact same reasons.
PEP 238 you quoted (thanks) says this about how to make //-less integer division : “Use […] divmod(x, y) for int division” (“int division” ! it doesn’t even say “floor division” !).
So, they clearly considered that divmod[0] should return the same as //.
Then, it also says : “If you know your integers are never negative, you can use int(x/y)”, which is music to my ears.

Those are probably covered by the “float arithmetics are clonky” umbrella. As far as I understand Tim Peter’s explanation, the occasional result of 15.0 is due to C implementation details or even system-specific conditions (correct me if I’m wrong).
So, I think it’s okay if a//b is sometimes slightly different to floor(a/b) depending on nebulous conditions that were never documented, never guaranteed, and possibly inconsistent across implems and systems. In any case, a//b would be the same as int(a/b) for positive results, that’s probably a better standard to be consistent with.
I’d refer to the documentation for divmod in float cases, which is almost unreadable if you didn’t major maths but explicitly gives a noncommitment clause for the exact value returned.
And funnily enough, divmod[0] in float cases is described there as being usually math.floor(a / b) :upside_down_face:

If that matters to people, and if some weird edge case uses where float-floordiv is actually useful were to show up, we could always add a math.floordiv function that would return the float.

That’s absolutely true.

Oh, you mean the library which respects dunders so much that __bool__ raises an exception ? (:
If they use // and rely on it returning a float, that’s one thing to consider. Other than that, I think these fine float-managing people would see no problem to continue using float-floordiv.
np.floor returns a float when math.floor returns an int anyway, so I hardly think it would be an issue if np.ndarray.__floordiv__ returns a float and float.__floordiv__ returns an int.