61

I have recently noticed that int() rounds a float towards 0, while integer division rounds a float towards its floor.

for instance:

-7 // 2 == -4
int(-7/2) == -3

I have read the documentation which specifies:

class int(x, base=10)

Return an integer object constructed from a number or string x, or return 0 if no arguments are >given. If x is a number, return x.__int__(). For floating point numbers, this truncates towards zero.

and:

floor division

Mathematical division that rounds down to nearest integer. The floor division operator is //. For example, the expression 11 // 4 evaluates to 2 in contrast to the 2.75 returned by float true division. Note that (-11) // 4 is -3 because that is -2.75 rounded downward. See PEP 238.

But it seems illogical for me that 2 similar operations (float division to integer) should return different results.

Is there any motivation for the differences between the functions?

Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
Isdj
  • 1,835
  • 1
  • 18
  • 36
  • Relevant link: http://python-history.blogspot.com/2010/08/why-pythons-integer-division-floors.html – dan04 Dec 12 '19 at 22:27

3 Answers3

70

Consistency.

You'll need to follow some very basic and seemingly irrelevant explanations to understand it.

In school you have learned division with a remainder. And you have done calculations like this:

8 ÷ 4 = 2 R 0
7 ÷ 4 = 1 R 3
6 ÷ 4 = 1 R 2
5 ÷ 4 = 1 R 1
4 ÷ 4 = 1 R 0
3 ÷ 4 = 0 R 3
2 ÷ 4 = 0 R 2
1 ÷ 4 = 0 R 1
0 ÷ 4 = 0 R 0
        ^------ This is the result of x // 4
            ^-- This is the result of x % 4 (modulo)

Later, you have learned divisions for real numbers:

8 ÷ 4 = 2.0
7 ÷ 4 = 1.75
6 ÷ 4 = 1.5
5 ÷ 4 = 1.25
4 ÷ 4 = 1.0
3 ÷ 4 = 0.75
2 ÷ 4 = 0.5
1 ÷ 4 = 0.25
0 ÷ 4 = 0.0
        ^--- Note that the number in front of the . is int(x/4)

Until this point, you might believe that x // 4 and int(x/4) always give the same result. That's your current understanding of the situation.

However, have a look what happens in the integer division: the number behind R cycles from 3, 2, 1 to 0 and then restarts: 3, 2, 1, 0. The number in front of the R decreses every 4th step.

So, how will it go on?

 8 ÷ 4 =  2 R 0
 7 ÷ 4 =  1 R 3
 6 ÷ 4 =  1 R 2
 5 ÷ 4 =  1 R 1
 4 ÷ 4 =  1 R 0
 3 ÷ 4 =  0 R 3
 2 ÷ 4 =  0 R 2
 1 ÷ 4 =  0 R 1
 0 ÷ 4 =  0 R 0
-1 ÷ 4 = -1 R 3
         ^------ We have to decrease now, because we already have 0 four times
              ^-- We have to restart the cycle at 3

At the same time, the real number division gives us:

-1 ÷ 4 = -0.25
          ^----- There is still a 0 in front of the .

That's why -1 // 4 gives -1 but int(-1/4) gives 0.

Is there any motivation for the differences between the functions?

Well, they serve different purposes: // is part of an integer calculation with remainders and int() gives you the part in front of the . of a real number operation.

You decide what you want to calculate, then you decide which operator to use in Python to get the correct result.

Good question. Keep on learning.

Thomas Weller
  • 55,411
  • 20
  • 125
  • 222
  • 14
    In practice, this allows for a trick: If you have -1 sweets and you give it away to 4 friends, then there will be 3 sweets remaining. Great, isn't it? You only need to find out how to own -1 sweets. – Thomas Weller Dec 12 '19 at 12:40
  • 1
    It does create some sort of consistency yet as far as i understand the motivation in adding the ```//``` operator in python 3 is in order to avoid forcing the use of int(float). If this isn't the case, when should I choose to implement using ```int()``` and when should I implement using ```//``` – Isdj Dec 12 '19 at 12:46
  • Do you have any citable source claiming that `//` ought to replace `int()`? – Thomas Weller Dec 12 '19 at 12:53
  • 1
    Ok, then it's just a wrong assumption. That's nothing bad, as long as you test your assumptions for correctness, which probably fails in 50% of the cases (at least it does for me). I added some words about it in the answer. – Thomas Weller Dec 12 '19 at 12:58
  • 2
    @IsaacDj you may want to [read this](https://www.python.org/dev/peps/pep-0238/) for the story behind the "floor division" operator. – bruno desthuilliers Dec 12 '19 at 13:18
  • @EricLippert: oh no. Another broken thing in C#. – Thomas Weller Dec 12 '19 at 20:42
  • @ThomasWeller: I made an error in my previous comment and deleted it, but I would not describe the behaviour as an error in C#. It is bizarre that Python does not maintain the invariant that `x//y` is equal to `-((-x)//y)`. – Eric Lippert Dec 12 '19 at 20:43
  • The deleted comment correctly noted that Python maintains the invariant that if `r` is `x%y` and `q` is `x//y`, then `q*y+r` is equal to `x`, but I got a few of the other details wrong. This is a sensible invariant to maintain, but it is unfortunate that Python's choice of "floor rounding" rather than "towards zero rounding" means that the magnitude of the result depends on the signs of the operands. – Eric Lippert Dec 12 '19 at 20:45
  • If this subject interests you, you might find this question on "ceiling rounding" and the various answers to it amusing: https://stackoverflow.com/questions/921180/how-can-i-ensure-that-a-division-of-integers-is-always-rounded-up/926806#926806. It is astonishing how many wrong answers were posted in a pretty short amount of time. – Eric Lippert Dec 12 '19 at 20:48
  • 1
    @EricLippert: I don't think it's bizarre. We can't assume that a lossy operation provides the same result as for a precise operation. Spoken in code: `Math.Floor(3.23) != -Math.Floor(-3.23)` For the same reason`-((-x)//y)` needn't equal `x//y`. – Thomas Weller Dec 12 '19 at 20:53
  • 1
    @EricLippert: I can only think of one time in my code when the invariant pair `(-x)/y == -(x/y)` and `(-x)%y == -(x%y)` would have been useful, and many where `(x+y)/y = (x+y)+1` and `(x+y)%y == x%y` would have been much more useful. Ironically, in the one situation where the former would have been useful, the compiler's code for `x/2` was so much slower than a manual adjust and shift that I had to use the latter anyhow. – supercat Dec 12 '19 at 22:06
  • Your extrapolation to -1 might make sense if you assume beforehand that the `//` operator represents this part before the rest (`R`). But isn't the question exactly why you would assume that? I naturally wouldn't. Especially since the result of `a // b` is always of type `int`, which also suggests that the value is `int(a/b)`. To me at least. – Jeronimo Jan 06 '20 at 15:02
  • @Jeronimo: they are not the same; `//` is the floor division operator and produces an integer value. `/` is the division operator and produces a float value. They are distinct operators because they produce distinct results. Then applying `int()` to a float value is entirely separate from those operators; `int(floatvalue)` takes the integer portion of the float number. Flooring is a very specific operation, it takes the *next lower integer*, so `-2.5`, floored, is `-3`. That’s not the same thing as taking the integer portion of `-2.5`, which is `-2`. – Martijn Pieters Jan 06 '20 at 22:37
  • @Jeronimo: so `a // b` is really `int(math.floor(a / b))`. – Martijn Pieters Jan 06 '20 at 22:43
  • @MartijnPieters Yes I know what they are, but the question remains **why** it is that `//` is the "floor division" and not the "int division". Intuitively in terms of usefulness I agree with OP that it should be the other way round, and that's not reflected in this otherwise good answer. – Jeronimo Jan 07 '20 at 08:39
  • @Jeronimo: why would `//` do that? That's already covered by `int(a / b)`. Note that in Python 2, if `a` and `b` are both integers, `/` already floored the result of the division, it was just entirely confusing that the operator changed behaviour when at least one of the operands was a float. See the [PEP proposing adding the new operand](https://www.python.org/dev/peps/pep-0238/). – Martijn Pieters Jan 07 '20 at 11:06
  • @ThomasWeller Cool, if Alice owns `-1` sweets, perhaps she owes `1` sweet to her friend Bob. Going by the above premise, and the pattern from your answer `(-1 ÷ 4 = -1 R 3)`, if Alice decides to "give it away to 4 friends", she would be giving "-1" sweets to each of her four friends, i.e. now four people each owe "-1" sweets to Alice. Using one of these four sweets to settle her initial debt with Bob, she can keep the remaining three sweets for herself, and thus end up with "3 sweets remaining" lol – Ardent Coder Jul 14 '21 at 13:39
7

I would say that your observation that those 2 operations should be intuitively similar is expected since on positive numbers they behave identically. But if you look at their origins (one comes from mathematics and the other from computer science) then it makes more sense their different behavior.

You can look behind there concepts:

  • Floor division aka the floor function applied to the math division
  • Type conversion/Type casting

==================================================================

I)Floor division aka the floor function applied to the math division

The floor function is a very well established concept in mathematics.

From mathworld.wolfram:

The floor function |_ x_ |, also called the greatest integer function or integer value (Spanier and Oldham 1987), gives the largest integer less than or equal to x. The name and symbol for the floor function were coined by K. E. Iverson (Graham et al. 1994)

So floor division is nothing more than floor function applied to the math division. The behavior is very clear, "mathematically precise".

II)Type conversion/Type casting

From wikipedia:

In computer science, type conversion, type casting, type coercion and type juggling are different ways of changing an expression from one data type to another.

In most of the programming languages, the casting form float to integer is applied by rounding rule (so there is a convention) :

  • Round toward 0 – directed rounding towards zero (also known as truncation)

Rounding rule according to IEEE 754.


So, in other words, the reason for the difference between integer division and float to int conversion in python is a mathematical one, here are some thoughts from Guido van Rossum (I guess I do not have to introduce him :D) (from the blog The history of Python, article "Why Python's Integer Division Floors")

This disturbs some people, but there is a good mathematical reason. The integer division operation (//) and its sibling, the modulo operation (%), go together and satisfy a nice mathematical relationship (all variables are integers):

a/b = q with remainder r

such that

b*q + r = a and 0 <= r < b

(assuming a and b are >= 0).

kederrac
  • 16,819
  • 6
  • 32
  • 55
0

Based on the answer by @thomas-weller I also found myself a maybe novice explanation for the floor division result from his example of division with a remainder:

  • 7 / 4 = 1 R 3 = 1 + 0,75 = 1.75

  • -> R3 = 3/4 = 0.75

  • -1 /4 = -1 R 3 = -1 + 0,75 (R3) = -0.25

s_horus
  • 1
  • 3