pythonfloating-pointoperatorsdivisionfloor-division

How is floor division not giving result according to the documented rule?


>>> 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?


Solution

  • 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.