14

In Python 2.7 and 3.x, why does integer division give me a non-correct number when dividing by a number 0 < x < 1?

Negative numbers -1 < x < 0 even work correctly:

>>> 1//.1
9.0
>>> 1//-.1
-10.0

I understand that integer division with a negative (or positive) number rounds toward negative infinity, however I would have thought 1//.1 should result in 10.0 since 1 can be divided by .1 without remainder.

Dimitris Fasarakis Hilliard
  • 150,925
  • 31
  • 268
  • 253
Bryce Guinta
  • 3,456
  • 1
  • 35
  • 36
  • 4
    Floor divison. See this post: http://stackoverflow.com/questions/5535206/negative-integer-division-surprising-result – WGS Oct 01 '16 at 21:18
  • 3
    floor(1/.1) should be equal to 10 though – Bryce Guinta Oct 01 '16 at 21:21
  • 6
    possibly another artifact of [Is floating point math broken?](http://stackoverflow.com/questions/588004/is-floating-point-math-broken) – Tadhg McDonald-Jensen Oct 01 '16 at 21:22
  • That is not floor division. That is flooring *after* division. One can argue that it's the same. Others can chip in -- I believe it's not but I don't wanna docu dive right now. – WGS Oct 01 '16 at 21:22
  • 3
    @Gilgamesh That does not explain why `1 // 0.1` is not the same as `floor(1 / 0.1)` – zvone Oct 01 '16 at 21:22
  • @Gilgamesh I think that, when `//` is applied to a long and a float, the float method ends up being used, which does seem to floor after division. –  Oct 01 '16 at 21:24
  • 2
    @poke I don't think this is a duplicate of that question. you'd expect `1//0.1 == math.floor(1/0.1)` but it doesn't here. – Tadhg McDonald-Jensen Oct 01 '16 at 21:26
  • @Tadhg: I would be unsurprised, but I wouldn't *expect* it, since I know better than to expect inexact calculations to be exact. –  Oct 01 '16 at 21:29
  • 4
    @Hurkyl There is no need to *"expect"* anything, but if `1//0.1 != math.floor(1/0.1)`, this question deserves an answer which says why (or at least be a duplicate of a question which explains that detail). – zvone Oct 01 '16 at 21:31
  • Nominating to reopen even if I voted to close as duplicate as @zvone has a point. The link I presented is not sufficient to answer this post-edit anyway. – WGS Oct 01 '16 at 21:34
  • This is really just a different twist of the floating math broken umbrella. I'd be quite inclined to VTC this when it gets off the Hot questions List (after more eyes see it since it is interesting) to add it to the duplicate chain leading back to the generalized answer. – Dimitris Fasarakis Hilliard Oct 01 '16 at 22:44
  • @zvone: When I commented, I had not realized that the post that 'won' the initial duplicate reason was not the one about inexact arithmetic. –  Oct 01 '16 at 23:52
  • @Hurkyl It seems that cPython uses `fmod` for flooring float division, instead of doing the slower `math.floor(a/b)` and everyone is satisfied with "floats are imprecise" explanation. I am not convinced ;) It actually looks like a shortcut in implementation. But in the end, we all have to live with *"floats are imprecise"* whenever we use floats, so it does not matter. – zvone Oct 02 '16 at 00:04

1 Answers1

27

What you’re seeing here is essentially the effect of the difference between “normal” division using / and flooring division with //.

What’s also always important to keep in mind is the general issue with floating point arithmetic which is a certain imprecision just because of how they work. In those situations, it’s always good to use the decimal module to check what’s actually going on. So let’s look at what you are doing here:

First of all, .1 is already not precise:

>>> Decimal(.1)
Decimal('0.1000000000000000055511151231257827021181583404541015625')

So, let’s look at the actual result of the divisions:

>>> Decimal(1) / Decimal(.1)
Decimal('9.999999999999999444888487687')
>>> 1 / .1
10.0

As you can see, the normal division using / does not exactly give you 10 with floating point arithmetic. But it’s really close. That’s why, when you use normal floats, you actually get back 10 (since the division imprecision is immediately lost in the imprecision of the number type).

When using flooring division, the result is floored before the imprecision is corrected, so that’s why you get 9:

>>> Decimal(1) // Decimal(.1)
Decimal('9')
>>> 1 / .1
10.0

With negative numbers, the flooring effect is the opposite direction, as explained in that other question:

>>> Decimal(1) / Decimal(-.1)
Decimal('-9.999999999999999444888487687')
>>> 1 / -.1
-10.0
>>> Decimal(1) // Decimal(-.1)
Decimal('-9')
>>> 1 // -.1
-10.0
Community
  • 1
  • 1
poke
  • 369,085
  • 72
  • 557
  • 602
  • 3
    Another good example is `3 // -.3`, where it "**fails**" with a negative number, resulting in `-11.0`. – Stefan Pochmann Oct 01 '16 at 21:45
  • note: `Decimal("1") // Decimal("0.1")` Gives `Decimal("10")` since it can represent decimal numbers exactly. Decimal preserves the given numbers exactly, so giving a string to decimal may be more precise for human input – Bryce Guinta Mar 01 '18 at 23:01