# Integer division

**URL:** <https://discuss.python.org/t/integer-division/23140>\
**Category:** Python Help\
**Created:** [January 25, 2023, 6:32am UTC](https://discuss.python.org/t/integer-division/23140 "2023-01-25T06:32:34Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![Dumara](https://avatars.discourse-cdn.com/v4/letter/d/c2a13f/32.png) [@Dumara](https://discuss.python.org/u/Dumara)\
**Post date:** [January 25, 2023, 6:32am UTC](https://discuss.python.org/t/integer-division/23140/1 "2023-01-25T06:32:34Z")

</div>

What is the result of this code: print(6 // 0.4)  
I thought the answer is 15.0 but I am getting 14.0. Any explanation for this answer I will appreciate.

---

<div class="post-metadata">

**Author:** ![PythonCHB](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/pythonchb/32/2551_2.png) [@PythonCHB](https://discuss.python.org/u/PythonCHB)\
**Post date:** [January 25, 2023, 6:46am UTC](https://discuss.python.org/t/integer-division/23140/2 "2023-01-25T06:46:03Z")

</div>

This is a really good question!

I _think_ what’s going on is that when 0.4 is converted to binary floating point, you get a value a tiny bit bigger than 0.4:

```python
In [40]: f"{0.4:.40f}"
Out[40]: '0.4000000000000000222044604925031308084726'

```

So 6 / 0.4 is a tiny bit smaller than 15.0, and thus gets truncated to 14.0

However, (and now I’m out of my depth with FP) – when you actually compute 6 / 0.4, you do get 15.0 (which is exactly representable in binary) as far as I can tell:

```python
In [43]: f"{6 / 0.4:.60f}"
Out[43]: '15.000000000000000000000000000000000000000000000000000000000000'

```

So I don’t get why the rounding doesn’t occur the same way before the truncation.

I’m sure someone that gets FP better than I do can explain …

-CHB

---

<div class="post-metadata">

**Author:** ![rob42](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/rob42/32/8218_2.png) [@rob42](https://discuss.python.org/u/rob42)\
**Post date:** [January 25, 2023, 7:22am UTC](https://discuss.python.org/t/integer-division/23140/3 "2023-01-25T07:22:33Z")

</div>

If we say `x = 6` and `y = 0.4`

Then `x / y` would be `15`, but `//` is called floor division, so you get `14.0`, but what you don’t see is the remainder, which as a ratio, is less than ‘0.4’. You can see both the quotient and the remainder by using a function called `divmod()`:

`divmod(x, y) = (14.0, 0.3999999999999997)`

Perhaps a more convincing use case would be if you needed to return the number of days and hours for any given number of hours:

```python
day = 24 # number of hours in a day
hours = 172 # number of given hours
days, hrs = divmod(hours, day)
print(f"{days} Days, {hrs} hrs")

print(hours / day)
print(hours // day)

```

Output:

```auto
7 Days, 4 hrs
7.166666666666667
7

```

Edit done for minor code correction.

---

<div class="post-metadata">

**Author:** ![steven.daprano](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/steven.daprano/32/1083_2.png) [@steven.daprano](https://discuss.python.org/u/steven.daprano)\
**Post date:** [January 25, 2023, 3:21pm UTC](https://discuss.python.org/t/integer-division/23140/4 "2023-01-25T15:21:21Z")

</div>

There is no “integer division” in Python. We have **floor division** which does division, keeping the floor of the result.

In case you don’t know what floor and ceiling are in maths:

> **[Floor and Ceiling Functions](https://www.mathsisfun.com/sets/function-floor-ceiling.html)**
>
> Math explained in easy language, plus puzzles, games, quizzes, worksheets and a forum. For K-12 kids, teachers and parents.

Floor division with floats is weird, it is much easier to understand it with only integer arguments. `60//4` is exactly 15, as we expect.

As usual, the weirdness is because floats are _binary_ numbers, not decimal. The float `0.4` is not _exactly_ equal to the decimal number 0.4, it is actually a tiny bit larger:

```auto
>>> "%.50f" % 0.4
'0.40000000000000002220446049250313080847263336181641'

```

So we’re actually dividing by something a tiny bit bigger than the decimal 0.4. And that makes a difference.

- In real numbers, 6 divided by _exactly_ 0.4 gives exactly 15.

- But something a tiny bit bigger than 0.4 only goes **14 times** into 6, plus a reminder.

We can work out the remainder:

```auto
>>> 6 % 0.4
0.3999999999999997

```

Look at the remainder: it is _almost_ equal to 0.4, in fact it is the very next float just _under_ `0.4`:

```auto
>>> math.nextafter(0.4, 0.0)
0.39999999999999997

```

With true division, `6/0.4`, we get:

- 6 divided by something a tiny bit bigger than 0.4 gives something a tiny bit less than 15;
- but in this case, the division rounds up to `15.0` exactly.

If you are thinking this is all terribly complicated, you are right.

---

<div class="post-metadata">

**Author:** ![PythonCHB](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/pythonchb/32/2551_2.png) [@PythonCHB](https://discuss.python.org/u/PythonCHB)\
**Post date:** [January 27, 2023, 7:42am UTC](https://discuss.python.org/t/integer-division/23140/5 "2023-01-27T07:42:06Z")

</div>

What still confuses me here is this:

```auto
In [73]: f"{0.4:.40f}"
Out[73]: '0.4000000000000000222044604925031308084726'

```

OK, so the binary representation of the the literal 0.4 is a tiny bit larger than the real number 0.4 – got it. So then 6.0 / 0.4 would be expected to be a tiny bit smaller than 15.0:

```auto
In [75]: 6.0 / 0.4
Out[75]: 15.0

```

but a tiny bit smaller (as pointed out, as close as you can get in 64 bit float) would be displayed by python as a rounded 15.0.

However:

```auto
In [77]: f"{6.0 / 0.4:.40f}"
Out[77]: '15.0000000000000000000000000000000000000000'

```

so it seems to actually be 15.0, and exactly the same as what the literal 15.0 creates:

```auto
In [82]: (6.0 / 0.4) == 15.0
Out[82]: True

```

IIUC, small integers are exactly represented in FP – so it seems when we do the division we get exactly 15. So there seems to be some sort of rounding that’s going on when we do the plain division that’s not being done before the floor is taken in floor division – and that has me confused.

NOTE: the days, hours example reminded me of code I wrote years ago that converted latitude and longitude from decimal (float) values to degrees, minutes, seconds. The simple way, using % and the like, for certain values would result in things like:

9 degrees, 23 minutes 60 seconds  
when that should be 24 minutes, zero seconds.

Sorry I don’t remember the particulars at the moment, but for certain values, you had the issue that the FP value was a tiny bit under 60, so it didn’t add another minute,but then rounded up to 60 for display.

I only caught it because I had arbitrarily chosen a number in a test that triggered the issue.

---

<div class="post-metadata">

**Author:** ![tim.one](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/tim.one/32/282_2.png) [@tim.one](https://discuss.python.org/u/tim.one)\
**Post date:** [January 27, 2023, 9:20am UTC](https://discuss.python.org/t/integer-division/23140/6 "2023-01-27T09:20:36Z")

</div>

> [@PythonCHB](#):
>
> IIUC, small integers are exactly represented in FP – so it seems when we do the division we get exactly 15. So there seems to be some sort of rounding that’s going on when we do the plain division that’s not being done before the floor is taken in floor division – and that has me confused.

Yes, integers with absolute value \<= 2\*\*53 are represented exactly in IEEE-754 double precision (CPython’s float type).

`6.0 / 0.4` is exactly 15.0 because _hardware_ division rounds it up to 15. 15.0 is the closest representable value to the infinitely precise result.

But Python’s floor division doesn’t care about that - it’s rounding down to the nearest integer in the direction of minus infinity. Hardware isn’t doing it - CPython is working hard to give you the true floor. To infinite precision, 6 divided by the float approximation of 0.4 is a little less than 15.0, so flooring that gives 14.0.

The lovely `mpmath` module lets you investigate things like this with ease, doing IEEE-style binary arithmetic with user-settable precision:

```python
>>> import mpmath
>>> print(mpmath.mp)
Mpmath settings:
  mp.prec = 53 [default: 53] # by default, 53 bits, same as a 754 double
  mp.dps = 15 [default: 15]
  mp.trap_complex = False [default: False]
>>> mpmath.mp.prec = 80 # boost precision to 80 bits
>>> print(mpmath.mp)
Mpmath settings:
  mp.prec = 80 [default: 53] # we changed this
  mp.dps = 23 [default: 15]
  mp.trap_complex = False [default: False]
>>> mpmath.mpf(0.4) # the number in question
mpf('0.40000000000000002220446049')
>>> 6.0 / mpmath.mpf(0.4) # showing that the infinitely precise result is < 15
mpf('14.999999999999999167332732')
>>> mpmath.mp.prec = 53 # but that rounds up to 15 under 53-bit precision
>>> 6.0 / mpmath.mpf(0.4)
mpf('15.0')

```

---

<div class="post-metadata">

**Author:** ![steven.daprano](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/steven.daprano/32/1083_2.png) [@steven.daprano](https://discuss.python.org/u/steven.daprano)\
**Post date:** [January 27, 2023, 10:25am UTC](https://discuss.python.org/t/integer-division/23140/7 "2023-01-27T10:25:32Z")

</div>

Correct, small integers are all exact in 64-bit floating point, but that’s not why `6.0/0.4` evaluates to exactly 15.0.

Under IEEE-754 maths, calculations are required to be correctly rounded. Anyone who is old enough to remember computer arithmetic before IEEE-754 can probably tell you many horror stories, such as

- x != y but x - y = 0.0
- x == y but x/y != 1.0

These abominations are impossible in IEEE-754 maths. But I digress.

I’m not an expert here, but my understanding is that arithmetic in IEEE-754 [must use an extra three bits](https://pages.cs.wisc.edu/~markhill/cs354/Fall2008/notes/flpt.apprec.html). So when we divide two 64-bit floats, the calculation actually uses 67 bits for the calculation, and then round the result back down to 64 when done.

When we calculate `6.0/0.4` we’re actually computing

```
6.0/(0.4 + d)

```

for some tiny value d, which works out as `15.0 - e` for some other tiny value e. In _this specific_ case, it just so happens that the difference between the true value 15.0 and the computed value `15.0 - e` is small enough that when the three guard digits are rounded off, the result ends up being the true value 15.0.

But that isn’t guaranteed to always happen with every division:

```
(6*7)/(0.4*7) # Mathematically exact result = 15.0
--> but returns 14.999999999999998

```

IEEE-754 semantics guarantee that when you divide two _binary_ numbers, the result is always correctly rounded in _binary_. That doesn’t necessarily mean that the result looks correct in decimal, but sometimes we get lucky:

- the exact value 6.0
- when divided by the inexact value 0.4
- gives a value slightly less than 15.0 when using 3 extra bits
- which when rounded back to 64 bits ends up being the exact value 15.0
- and multitudes rejoice.

Whereas the alternate calculation ends up rounding down to something a tad under the mathematically true value.

---

<div class="post-metadata">

**Author:** ![PythonCHB](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/pythonchb/32/2551_2.png) [@PythonCHB](https://discuss.python.org/u/PythonCHB)\
**Post date:** [January 27, 2023, 6:04pm UTC](https://discuss.python.org/t/integer-division/23140/8 "2023-01-27T18:04:18Z")

</div>

Thanks Tim, that does it explain it, though to my naive eye, python’s floor division is not doing the right (or the best) thing here – the results woul be less surprising, if not more correct if:

`a // b` gave the same result as `math.floor( a / b)`, which it is does not:

```auto
In [137]: 6 // 0.4 == math.floor(6 / 0.4)
Out[137]: False

```

Also in my naive view, I’m surprised `//` isn’t in fact implemented that way 🙂

As far as I could tell with a quick search IEEE 754 doesn’t define “floor division” – I wonder what it would specify if it did?

NOTE: before floor division was defined in Python, I imagine folks would have done `math.floor(a / b)` – so an incompatibility was introduced.

In fact, if you use integers too large to be exactly represented by floats, then integer division IS different than floor division, which strikes me as sub-optimal:

```auto
In [148]: a
Out[148]: 60000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

In [149]: b
Out[149]: 4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

In [150]: a / b
Out[150]: 15.0

In [151]: float(a) // float(b)
Out[151]: 14.0

```

Granted, for practical purposes, unlikely to come up, but still.

---

<div class="post-metadata">

**Author:** ![Rosuav](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/rosuav/32/3429_2.png) [@Rosuav](https://discuss.python.org/u/Rosuav)\
**Post date:** [January 27, 2023, 7:38pm UTC](https://discuss.python.org/t/integer-division/23140/9 "2023-01-27T19:38:33Z")

</div>

> [@PythonCHB](#):
>
> Thanks Tim, that does it explain it, though to my naive eye, python’s floor division is not doing the right (or the best) thing here – the results woul be less surprising, if not more correct if:
> 
> `a // b` gave the same result as `math.floor( a / b)`, which it is does not:
> 
> ```auto
> In [137]: 6 // 0.4 == math.floor(6 / 0.4)
> Out[137]: False
> 
> ```
> 
> Also in my naive view, I’m surprised `//` isn’t in fact implemented that way 🙂

A better correlation would be that `a // b` should be the same as `divmod(a, b)[0]`, that `a % b` should be the same as `divmod(a, b)[1]`, and then that divmod is defined using this rule:

```auto
q, r = divmod(a, b)
assert q * b + r == a

```

as is stated in divmod’s docstring. As far as I know, this should be true of all numeric types. (Sadly, I can’t use divmod with strings, even if I subclass `str` such that `x // y` \<=\> `x.split(y)` - it still objects to the objects I’m feeding it. Awwww.)

The downside is, it’s notably harder to then explain floor division on its own; you have to simultaneously explain modulo.

---

<div class="post-metadata">

**Author:** ![tim.one](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/tim.one/32/282_2.png) [@tim.one](https://discuss.python.org/u/tim.one)\
**Post date:** [January 27, 2023, 8:02pm UTC](https://discuss.python.org/t/integer-division/23140/10 "2023-01-27T20:02:08Z")

</div>

> [@PythonCHB](#):
>
> As far as I could tell with a quick search IEEE 754 doesn’t define “floor division” – I wonder what it would specify if it did?

A fully compliant 754 implementation needs to support the 4 distinct rounding modes defined by that standard. “To minus infinity” rounding is what it calls what we’re calling “floor” here. Its “to plus infinity” rounding is what we call “ceiling”. But core Python does not support any way to get at your hardware’s rounding mode setting. If it did, and you asked for “to minus infinity” rounding, your hardware division would also return a result less than `15.0` for `6 / 0.4`.

```python
>>> import mpmath
>>> mpmath.fdiv(6.0, 0.4)
mpf('15.0')
>>> mpmath.fdiv(6.0, 0.4, rounding='floor')
mpf('14.999999999999998')

```

Essentially all 754 operations are defined to return the infinitely precise result subject to one rounding at the end. There is no argument to be made about the “correct” result for `6 / 0.4`. It’s precisely defined for each rounding mode.

A more fruitful question would be to wonder why Python supports “floor division” for floats at all. It’s very useful for ints, but ints have many pleasant mathematical properties floats lack. In a similar vein, the closely related definition for integer `%` results isn’t really what floats want either.

So it goes. A great many languages are seduced by the observation that, mathematically, the integers are a subset of the reals. That’s where “numeric tower” wishful thinking comes from. But machine floats are very far from being “the reals”. Fixed-precision machine floats are in fact a strangely lumpy finite subset of the rationals, augmented with weird 😉 “infinity” and “NaN” special cases.

> [@PythonCHB](#):
>
> NOTE: before floor division was defined in Python,

Floor division was part of Python from day #1, so there is no “before”. However, for floats, to get at it you needed to spell it at first as `divmod(x, y)[0]`.

> [@PythonCHB](#):
>
> I imagine folks would have done `math.floor(a / b)` – so an incompatibility was introduced.

Nothing about the meaning of that fragment has changed. `/` has _always_ meant “true division” for floats. Although I’m lying a bit: `math.floor()`, until quite recently, returned a float instead of an int, mirroring what the C libm `floor()` does. Changing that was not, IMO, a good idea either.

---

<div class="post-metadata">

**Author:** ![steven.daprano](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/steven.daprano/32/1083_2.png) [@steven.daprano](https://discuss.python.org/u/steven.daprano)\
**Post date:** [January 28, 2023, 1:46am UTC](https://discuss.python.org/t/integer-division/23140/11 "2023-01-28T01:46:15Z")

</div>

I think you are surprised because you can’t get past the fact that the literal `0.4` looks like the exact decimal fraction 4 over 10, and so you think `6/0.4` is exactly correct and `6//4` has rounding error.

This is where the convenience of being able to enter and display _binary_ floats in decimal bites us on the derrière and gives us the wrong intuitions about floats. It makes us surprised about things we shouldn’t be surprised about, and take for granted results which should be astonishing.

Instead of using the literal `0.4`, let’s suppose your divisor was the result of some long, complicated computation, where you have _no expectation_ that it would end up exactly one fifteenth of 6:

Here is an example of such a complicated computation, which is _totally_ not contrived 😉

```
x = 7205759403792794.0 * 5.551115123125783e-17

```

If we compute `6//x` without first inspecting the value of x, and get 14.0, we’re not surprised in the least. We had to get some whole number, and 14.0 is no more or less surprising than any other.

If we now inspect the value of x to _full precision_, we see something like 0.40000…7263336181640625 and we’re hardly surprised that the number we computed is a “weird decimal”, nearly all numbers are “weird” in the sense of having a bunch of digits with no obvious pattern.

The only strange thing about this x is that there’s a bunch of 0s in a row in the middle of the number, but that’s not _that_ surprising. Lots of numbers have a bunch of zeroes in them.

So now let’s take that “weird decimal” x, and divide it into 6. It would be some coincidence if it happened to divide _exactly_ into 6 and give a whole number result, right?

In fact, if we look at the long tail of digits in the exact decimal representation …7263336181640625 it would be _astonishing_ if it divided exactly, we ought to get something like 14.9999999…987 or 15.000000…132 or something.

And yet, as if by magic, the rounding error in `6/x` disappears and leaves us with exactly 15.0.

I don’t know about anyone else, but I find it far more amazing that binary floating point arithmetic _ever_ gives the mathematically correct values I expect in decimal than the fact that sometimes it doesn’t.

---

<div class="post-metadata">

**Author:** ![PythonCHB](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/pythonchb/32/2551_2.png) [@PythonCHB](https://discuss.python.org/u/PythonCHB)\
**Post date:** [January 28, 2023, 4:35am UTC](https://discuss.python.org/t/integer-division/23140/12 "2023-01-28T04:35:59Z")

</div>

> [@steven.daprano](#):
>
> I think you are surprised because you can’t get past the fact that the literal `0.4` looks like the exact decimal fraction 4 over 10, and so you think `6/0.4` is exactly correct and `6//4` has rounding error.

Well, no, I’m not – I thought I made it clear that I was surprised because I expected “floor division” to be the same as taking the floor after dividing – i.e. floor(a / b) – and it’s not, which Tim explained quite nicely.

I think Tim’s point about maybe floor division being a tricky idea for floats is a good one.

For that matter, I’ve been surprised more than once that a\_float // a\_float returns a float – isn’t it always supposed to be an integral value?

if `floor(a_float)` returns an int, then why not floor division?

(Tim argues that `floor()` shouldn’t return and int – which would at least be consistent)

I’m particularly surprised, because `in_int // an_int` does return an int, and the whole thing was created to clear up the surprises that we got when the result of an operation depended on the type (rather than the value) of the operators.

This has practical considerations – I’ve often used // to compute an index – having to wrap a int() around the result is a bit annoying.

By the way, I may have been wrong, I think folks would expect that:

`a // b` would be the same as `int(a / b)` – which it also doesn’t.

Oh well.
