classification
Title: Clarify programming faq.
Type: enhancement Stage: patch review
Components: Documentation Versions: Python 3.7, Python 3.6
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: docs@python Nosy List: docs@python, kgashok, marco.buttu, mark.dickinson, r.david.murray, terry.reedy, wohlganger
Priority: normal Keywords:

Created on 2017-07-24 21:50 by terry.reedy, last changed 2017-09-18 14:29 by r.david.murray.

Pull Requests
URL Status Linked Edit
PR 2768 kgashok, 2017-07-24 21:53
Messages (7)
msg299023 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2017-07-24 21:50
https://docs.python.org/3/faq/programming.html#why-does-22-10-return-3
"Why does -22 // 10 return -3?
It’s primarily driven by the desire that i % j have the same sign as j. If you want that, and also want:

i == (i // j) * j + (i % j)

then integer division has to return the floor. C also requires that identity to hold, and then compilers that truncate i // j need to make i % j have the same sign as i.

There are few real use cases for i % j when j is negative. When j is positive, there are many, and in virtually all of them it’s more useful for i % j to be >= 0. If the clock says 10 now, what did it say 200 hours ago? -190 % 12 == 2 is useful; -190 % 12 == -10 is a bug waiting to bite."

A user noticed that '-190 % 12 == -10' is False, but would be True is '-' were inserted before '12', and posted https://github.com/python/cpython/pull/2768 to correct the presumed typo.

It is not a typo, and I will close the issue, but the text as is is confusing.  I propose replace "-190 % 12 == -10 is" with "if -190 % 12 were the mathematically equivalent -10, it would be" [a bug waiting to bite].  I don't like the 'bug' part because it would not be a bug, exactly, but it would be bug bait.  I am not sure how to improve it though.
msg299082 - (view) Author: Charles Wohlganger (wohlganger) * Date: 2017-07-25 13:47
Modulo is defined mathematically as the remainder of Euclidian division. I.E. a positive r where a % b = r is equivalent to a = b * x + r. I think it confuses the issue to say "-190 % 12 were the mathematical equivalent -10", when that is technically incorrect.

Computer modulo uses truncated division, which is why -a % b != a % -b.

"... compilers that truncate i // j need to make i % j have the same sign as i."
i % j has the same sign as j, not i. I believe that is the typo that has caused the confusion.

I would replace the last line with :
"-190 % 12 == -10 is wrong according to the C definition for computer modulo arithmetic."
msg299133 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2017-07-25 19:13
Terry: can you clarify which part you think is potentially confusing? I'm having a hard time seeing the text as confusing, but I suspect I'm too close to the subject matter to be able to tell.

Charles: I think you're missing the point (which does rather reinforce Terry's suggestion that this FAQ could be improved). You say:

> "-190 % 12 == -10 is wrong according to the C definition for computer modulo arithmetic."

But that's the point: for C (specifically C99[*]), -10 is the *correct* result from the operation -190 % 12. And that's exactly why this is a FAQ: Python is behaving differently from many other mainstream languages (C, Java, C++, C#, ...) here, so it's useful to understand the justification for this design decision.

For C in particular, this behaviour is mandated by section 6.5.5p6 of C99, which reads:

> When integers are divided, the result of the / operator is the algebraic
> quotient with any fractional part discarded. If the quotient a/b is
> representable, the expression (a/b)*b + a%b shall equal a.

The first part of this forces -190 / 12 to be -15 (the result of discarding the fractional part of the true quotient -15.833....); the second then forces -190 % 12 to be (-190) - (-15)*12, which is -10.

([*] In C89, the rounding direction of a/b for negative a, and hence the behaviour of a%b, was left implementation defined, but same-sign-as-a appears to have been the dominant behaviour.)
msg299136 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2017-07-25 19:46
I think Terry and his OP are reacting to the fact that "-190 % 12 == -10" looks like it is saying that that expression is True in Python, which it is not (and that's the point).

IMO, another issue is that "and then compilers that truncate i // j need to make i % j have the same sign as i." doesn't have enough connection in the naive reader's mind with "-190 % 12 == -10 is a bug waiting to bite" for easy comprehension.  

A potential fix to both these issues would be to say "if -190 % 12 were equal to -10 in Python (the way it is in most C programs), that would quite often be a bug waiting to happen in code using the % operator."
msg299232 - (view) Author: Marco Buttu (marco.buttu) * Date: 2017-07-26 14:05
Terry thanks for opening this issue.

The title of the FAQ makes me think that the section wants to clarify why -22 // 10 returns -3.  I am a bit confused, maybe because -22//10 == -3 does not surprise me, and so I do not understand the point :( 
 This seems to me a section about the module rather than floor division.

If the section wants to clarify the floor division behavior in Python, IMHO at the beginning of the section we have to explain why -22//10 == -3 may surprise the reader (it is not clear to me, so maybe it could not surprise other readers too), and then the reasons that justify why to have -22//10 == -3.
msg302419 - (view) Author: Ashok Bakthavathsalam (kgashok) * Date: 2017-09-18 08:14
So, what is the resolution on this?
msg302457 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2017-09-18 14:29
I think there is nothing to do here unless Mark likes my suggestion and/or someone comes up with an even better improvement.
History
Date User Action Args
2017-09-18 14:29:21r.david.murraysetmessages: + msg302457
2017-09-18 08:14:14kgashoksetnosy: + kgashok
messages: + msg302419
2017-07-26 14:05:11marco.buttusetnosy: + marco.buttu
messages: + msg299232
2017-07-25 19:46:43r.david.murraysetnosy: + r.david.murray
messages: + msg299136
2017-07-25 19:13:07mark.dickinsonsetmessages: + msg299133
2017-07-25 13:47:54wohlgangersetnosy: + wohlganger
messages: + msg299082
2017-07-25 08:04:43mark.dickinsonsetnosy: + mark.dickinson
2017-07-24 21:53:08kgashoksetpull_requests: + pull_request2904
2017-07-24 21:50:50terry.reedycreate