I know Python // rounds towards negative infinity and in C / is truncating, rounding towards 0.
And here's what I know so far:
-12 / 10 = -1 - 2 // c
-12 // 10 = -2 8 # python
12 / -10 = -1 2 // c
12 // -10 = -2 - 8 # python
12 / 10 = 1 2 //both
12 // 10 = 1 2
-12 / -10 = 1 - 2 //both
= 2 8
C :
1. m%(-n) == m%n
2. -m%n == -(m%n)
3. (m/n)*n m%n == m
python:
1. m%(-n) == -8 == -(-m%n)
2. (m//n)*n m%n == m
But why Python // choose to round towards negative infinity? I didn't find any resources explain that, but only find and hear people say vaguely: "for mathematics reasons".
For example, in Why is -1/2 evaluated to 0 in C , but -1 in Python? :
People dealing with these things in the abstract tend to feel that rouding toward negative infinity makes more sense (that means it's compatible with the modulo function as defined in mathematics, rather than % having a somewhat funny meaning).
But I don't see C 's / not being compatible with the modulo function. In C , (m/n)*n m%n == m also applies.
So what's the (mathematical) reason behind Python choosing rounding towards negative infinity?
CodePudding user response:
Although I can't provide a formal definition of why/how the rounding modes were chosen as they were, the citation about compatibility with the % operator, which you have included, does make sense when you consider that % is not quite the same thing in C and Python.
In C , it is the remainder operator, whereas, in Python, it is the modulus operator – and, when the two operands have different signs, these aren't necessarily the same thing. There are some fine explanations of the difference between these operators in the answers to: What's the difference between “mod” and “remainder”?
Now, considering this difference, the rounding (truncation) modes for integer division have to be as they are in the two languages, to ensure that the relationship you quoted, (m/n)*n m%n == m, remains valid.
Here are two short programs that demonstrate this in action (please forgive my somewhat naïve Python code – I'm a beginner in that language):
C :
#include <iostream>
int main()
{
int dividend, divisor, quotient, remainder, check;
std::cout << "Enter Dividend: "; // -27
std::cin >> dividend;
std::cout << "Enter Divisor: "; // 4
std::cin >> divisor;
quotient = dividend / divisor;
std::cout << "Quotient = " << quotient << std::endl; // -6
remainder = dividend % divisor;
std::cout << "Remainder = " << remainder << std::endl; // -3
check = quotient * divisor remainder;
std::cout << "Check = " << check << std::endl; // 27
return 0;
}
Python:
print("Enter Dividend: ") # -27
dividend = int(input())
print("Enter Divisor: ") # 4
divisor = int(input())
quotient = dividend // divisor;
print("Quotient = " str(quotient)) # -7
modulus = dividend % divisor;
print("Remainder = " str(modulus)) # 1
check = quotient * divisor modulus; # -27
print("Check = " str(check))
Note that, for the given inputs of different signs (-27 and 4), both the quotient and remainder/modulus are different between the languages but also that the restored check value is correct in both cases.
CodePudding user response:
But why Python
//choose to round towards negative infinity?
I'm not sure if the reason why this choice was originally made is documented anywhere (although, for all I know, it could be explained in great length in some PIP somewhere), but we can certainly come up with various reasons why it makes sense.
One reason is simply that rounding towards negative (or positive!) infinity means that all numbers get rounded the same way, whereas rounding towards zero makes zero special. The mathematical way of saying this is that rounding down towards −∞ is translation invariant, i.e. it satisfies the equation:
round_down(x k) == round_down(x) k
for all real numbers x and all integers k. Rounding towards zero does not, since, for example:
round_to_zero(0.5 - 1) != round_to_zero(0.5) - 1
Of course, other arguments exist too, such as the argument you quote based on compatibility with (how we would like) the % operator (to behave) — more on that below.
Indeed, I would say the real question here is why Python's int() function is not defined to round floating point arguments towards negative infinity, so that m // n would equal int(m / n). (I suspect "historical reasons".) Then again, it's not that big of a deal, since Python does at least have math.floor() that does satisfy m // n == math.floor(m / n).
But I don't see C 's
/not being compatible with the modulo function. In C ,(m/n)*n m%n == malso applies.
True, but retaining that identity while having / round towards zero requires defining % in an awkward way for negative numbers. In particular, we lose both of the following useful mathematical properties of Python's %:
0 <= m % n < abs(n)for allmandn; and(m k * n) % n == m % nfor all integersm,nandk.
These properties are useful because one of the main uses of % is "wrapping around" a number m to a limited range of length n.
For example, let's say we're trying to calculate directions: let's say heading is our current compass heading in degrees (counted clockwise from due north, with 0 <= heading < 360) and that we want to calculate our new heading after turning angle degrees (where angle > 0 if we turn clockwise, or angle < 0 if we turn counterclockwise). Using Python's % operator, we can calculate our new heading simply as:
heading = (heading angle) % 360
and this will simply work in all cases.
However, if we try to to use this formula in C , with its different rounding rules and correspondingly different % operator, we'll find that the wrap-around doesn't always work as expected! For example, if we start facing northwest (heading = 315) and turn 90° clockwise (angle = 90), we'll indeed end up facing northeast (heading = 45). But if then try to turn back 90° counterclockwise (angle = -90), with C 's % operator we won't end up back at heading = 315 as expected, but instead at heading = -45!
To get the correct wrap-around behavior using the C % operator, we'll instead need to write the formula as something like:
heading = (heading angle) % 360;
if (heading < 0) heading = 360;
or as:
heading = ((heading angle) % 360) 360) % 360;
(The simpler formula heading = (heading angle 360) % 360 will only work if we can always guarantee that heading angle >= -360.)
This is the price you pay for having a non-translation-invariant rounding rule for division, and consequently a non-translation-invariant % operator.
CodePudding user response:
From the python documentation (https://docs.python.org/2/reference/expressions.html):
The / (division) and // (floor division) operators yield the quotient of their arguments. The numeric arguments are first converted to a common type. Plain or long integer division yields an integer of the same type; the result is that of mathematical division with the ‘floor’ function applied to the result.
note in the last line it says that the floor function is applied to the result.
So let's see the behaviour of the floor function in python (https://docs.python.org/3/library/math.html):
math.floor(x) Return the floor of x, the largest integer less than or equal to x. If x is not a float, delegates to x.floor(), which should return an Integral value.
here the logic of the math.floor(x) function in python is to get the largest integer less than or equal to x and it's the same logic with the floor() function in c (https://www.cplusplus.com/reference/cmath/floor/).
But because the division operator in c depends on the operands datatypes so it just truncates the fractional part and yields only the integer part of the quotient of the division in case the two operands were integers (https://www.informit.com/articles/article.aspx?p=352857&seqNum=4):
Division Diversions: You have yet to see the rest of the story about the division operator (/). The behavior of this operator depends on the type of the operands. If both operands are integers, C performs integer division. That means any fractional part of the answer is discarded, making the result an integer. If one or both operands are floating-point values, the fractional part is kept, making the result floating-point.
So, in both languages the logic of floor is the same and is to round toward negative infinity but for python the floor-division actually calls the floor function unlike c division behaviour when dealing with two integers.
And for why floor is rounding toward negative infinity is because it is the most reasonable way to get the largest integer less than or equal to the number floor is applied to.
Take for example the number -6.8 and apply the floor function to it the result will be -7 now -7 is the largest integer less than or equal to -6.8 because we round toward negative infinity, but what if we just truncate the fractional part of the number?
It will be -6 which is actually bigger than our original number -6.8, So I think it depends on whether you want to truncate the fractional part or to get the floor of the quotient of the division.
CodePudding user response:
Both whole-number and real-number arithmetic define their division operators so that both of the following equivalences hold for all values of n and d.
(n d)/d = (n/d) 1
(-n)/d = -(n/d)
Unfortunately, integer arithmetic cannot be defined in such a way that both hold. For many purposes, the first equivalence is more useful than the second, but in most situations where code would be dividing two values, one of the following would apply:
Both values are positive, in which case the second equivalence is irrelevant.
The dividend is a precise integer multiple of the divisor, in which case both equivalences can hold simultaneously.
Historically, the easiest way to handle division involving negative numbers was to observe whether exactly one operand was negative, drop the signs, perform the addition, and then make the result negative if exactly one operand was. This would behave as required in both of the common situations, and would be cheaper than using an approach that would uphold the first equivalence in all cases, rather than only when the divisor was an exact multiple of the dividend.
Python shouldn't be viewed as using inferior semantics, but rather deciding that semantics which would generally be superior in cases that mattered would be worth making division slightly slower even in cases where the precise semantics wouldn't matter.
CodePudding user response:
"for mathematics reasons"
Consider the problem (common enough in video games) where you have an X-coordinate that can be negative, and want to translate it into a tile coordinate (let's suppose 16x16 tiles) and an offset within the tile
Python's % gives you the offset directly, and / gives you the tile directly:
>>> -35 // 16 # If we move 35 pixels left of the origin...
-3
>>> -35 % 16 # then we are 13 pixels from the left edge of a tile in row -3.
13
(And divmod gives you both at once.)
