9
>>> print (12//0.2)
59.0
>>> print(floor(12/0.2))
60

Why floor division is not working according to the rule in this case?

p.s. Here Python is treating 0.2 as 0.20000000001 in the floor division case So (12/0.2000000001) is resulting in 59.999999... And floor(59.999999999) outputting 59 But don't know why python is treating 0.2 as 0.2000000001in the floor division case but not in the division case?

  • That's weird. Do you know why `12//0.2` gives 59.0? – Maciej M Oct 26 '20 at 08:41
  • 2
    Here Python is treating `0.2` as `0.2000000000000001` So `(12/0.2000000000000001)` is resulting in `59.999999...` – Aashutosh Ranjan Oct 26 '20 at 09:06
  • I am not a Python expert so i can't awnser this question, but you may find more revelant information here https://docs.python.org/3/tutorial/floatingpoint.html – damjuve Oct 26 '20 at 09:27
  • I would assume that the magic happens in the print and it rounds the 59.999999.... to 60. By the way is floor now printing 59 or 60? "And floor(59.999999999) outputting 59" but in the core it prints 60 – Klamsi Oct 26 '20 at 09:34
  • @Klamsi That’s not what happens. – Konrad Rudolph Oct 26 '20 at 09:35
  • @KonradRudolph sure? http://effbot.org/pyfaq/why-are-floating-point-calculations-so-inaccurate.htm – Klamsi Oct 26 '20 at 09:47
  • @Klamsi Sure. `print(12 / 0.2 == 60)`. Even OP’s code already ensures this isn’t what’s happening because they’re printing the result of `floor`, not of the division itself. – Konrad Rudolph Oct 26 '20 at 09:50
  • I'm a bit confused with print floor = 60. For me this makes no sense. But in the text he says: "And floor(59.999999999) outputting 59" – Klamsi Oct 26 '20 at 09:54
  • @Klamsi OP missed a few 9’s in there: `59.999999999999999` is `60`. These numbers have the **same** representation Python. Not similar, *identical* (`59.999999999999999 is 60.0` yields `True`). `12 / 0.2` really is equal to `60`, it’s not being rounded. This is all as expected. The real question is why `60 // 0.2` doesn’t have the same result. – Konrad Rudolph Oct 26 '20 at 09:57

1 Answers1

9

The reason why 12 / 0.2 results in 60.0, is not because 0.2 is treated differently, but because the error in the floating point division cancels the error in the representation of 0.2. The float always has the same value (greater than decimal 0.2), but depending on the operations those errors will either accumulate or be cancelled.

In other cases the error is not completely cancelled and shows up in the result:

>>> (12 / (0.2 * 0.2)) * 0.2
59.99999999999999

In CPython integer division for these specific types (float // float after the first param is automatically converted) and relative magnitudes is performed as follows (see Python's source code for the full method):

mod = a % b
result = (a - mod) / b

If b was actually 0.2, then mod would be 0, but in floating point it is slightly larger, so mod is just under 0.2.

If you do this manually you can see how we end up with 59.0:

>>> a = 12.0
>>> b = 0.2
>>> mod = a % b
>>> mod
0.19999999999999934
>>> (a - mod) / b
59.0

The OP is also asking about the error in the floating point division, here's that as well:

The values (mantissa * base^exponent):

12:         1.1000000000000000000000000000000000000000000000000000 * 2^3
0.2:        1.1001100110011001100110011001100110011001100110011010 * 2^(-3)

Remember 0.2 is not really 0.2, it's 0.200000000000000011102230246251565404236316680908203125. The result of dividing 12 by a value that is > 0.2 should be < 60.

To divide the values, we divide the mantissa and subtract the exponent, so we get:

12 / 0.2:   0.1110111111111111111111111111111111111111111111111111111 * 2^6

But the last 3 bits don't fit into a double, which only has 53 bits for the mantissa (including the sign) and we're currently using 56.

Since the result starts with 0, we first normalise, multiplying the mantissa by 2 and subtracting one from the exponent. And then we have to round to the nearest 53 bit mantissa:

normalised: 1.110111111111111111111111111111111111111111111111111111 * 2^5
rounded:    1.1110000000000000000000000000000000000000000000000000 * 2^5

1.1110000000000000000000000000000000000000000000000000 * 2^5 is equal to 60.

The difference between the correct result (1.110111111111111111111111111111111111111111111111111111 * 2^5) and the closest value we can represent as a 64 bit double (1.1110000000000000000000000000000000000000000000000000 * 2^5) is the error in the floating point division.

Diego Veralli
  • 1,011
  • 6
  • 13