This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: odd floor division behavior
Type: behavior Stage: resolved
Components: Interpreter Core Versions: Python 3.6
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: Nathan.Goldbaum, ammar2, mark.dickinson, serhiy.storchaka, steven.daprano, tim.peters
Priority: normal Keywords:

Created on 2018-01-12 22:32 by Nathan.Goldbaum, last changed 2022-04-11 14:58 by admin. This issue is now closed.

Messages (5)
msg309873 - (view) Author: Nathan Goldbaum (Nathan.Goldbaum) Date: 2018-01-12 22:32
According to PEP 238:


"floor division will be implemented in all the Python numeric types, and will have the semantics of:

a // b == floor(a/b)

except that the result type will be the common type into which a and b are coerced before the operation."

But consider the following cases in Python 3.6.3:

>>> 0.9//0.1
8.0
>>> 0.8//0.1
8.0
>>> import math
>>> math.floor(0.9/0.1)
9
>>> math.floor(0.8/0.1)
8

Am I missing something?
msg309879 - (view) Author: Steven D'Aprano (steven.daprano) * (Python committer) Date: 2018-01-12 23:47
That does look at first glance like a bug in // to me. 0.9/0.1 is correctly rounded to 9.0 exactly, so flooring it should return 9 (as it does):

# Python 3.5 on Linux
py> 0.9/0.1 == 9
True
py> math.floor(0.9/0.1)
9

So I too would expect that 0.9//0.1 should evaluate as 9, not 8.
msg309880 - (view) Author: Ammar Askar (ammar2) * (Python committer) Date: 2018-01-13 00:00
Looks like floor division for floats call into the divmod function, which has the same behavior:

>>> 0.9 .__divmod__(0.1)
(8.0, 0.09999999999999998)
msg309881 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2018-01-13 00:07
This is the result of multiple roundings.

0.9 and 0.1 can't be represented exactly as floats. They are approximated by binary fractions.

>>> from fractions import Fraction
>>> Fraction(0.9)
Fraction(8106479329266893, 9007199254740992)
>>> Fraction(0.1)
Fraction(3602879701896397, 36028797018963968)

The result of the division of these fractions can't be represented as a float too.

>>> Fraction(0.9)/Fraction(0.1)
Fraction(32425917317067572, 3602879701896397)
>>> Fraction(0.9)/Fraction(0.1) - 9
Fraction(-1, 3602879701896397)
>>> float(Fraction(0.9)/Fraction(0.1) - 9)
-2.7755575615628914e-16
>>> 9 + float(Fraction(0.9)/Fraction(0.1) - 9)
9.0

It is slightly smaller than 9, but the nearest float value is 9.0. Thus the result of the division is rounded up to 9.0.

A similar issue already was opened several months ago. I don't remember the number.
msg309887 - (view) Author: Tim Peters (tim.peters) * (Python committer) Date: 2018-01-13 03:17
This report appears to be the same:

https://bugs.python.org/issue27463

One other thing to note:  the Python docs are sometimes unclear about whether expressions are intended to be interpreted as Python code or as mathematical expressions.  In:

"""
floor division will be implemented in all the Python numeric types, and will have the semantics of:

a // b == floor(a/b)
"""

note that it did _not_ say `math.floor(a/b)`.  `floor(a/b)` here is intended to be read as a mathematical (infinitely precise) specification.  As can be inferred from the output Serhiy posted, after the Python

a = 0.9
b = 0.1

the infinitely precise value of `a/b` is a tiny bit less than 9, so the infinitely precise value of `floor(a/b)` is exactly 8.  Which is what the Python `a // b` returns.

Personally, I wish Python 3 had changed the meaning of `//` (and `divmod()` and `%` - the three are deeply related) for floats, but too late for that now :-)
History
Date User Action Args
2022-04-11 14:58:56adminsetgithub: 76724
2018-01-13 03:17:59tim.peterssetmessages: + msg309887
2018-01-13 00:07:48serhiy.storchakasetstatus: open -> closed

nosy: + serhiy.storchaka
messages: + msg309881

resolution: not a bug
stage: resolved
2018-01-13 00:00:18ammar2setnosy: + ammar2
messages: + msg309880
2018-01-12 23:47:33steven.dapranosetnosy: + mark.dickinson, steven.daprano, tim.peters
messages: + msg309879
2018-01-12 22:32:48Nathan.Goldbaumcreate