◐ Shell
clean mode source ↗

Message 249307 - Python tracker

Hi, I'm trying to write the rationale of the changes that I wrote in pytime.h in Python 3.3-3.5. The rounding of microseconds or nanoseconds is tricky. The code changed a lot and we used and we are still using various rounding methods depending on the case...

Alexander Belopolsky wrote:
> I believe the correct mode is "ROUND_HALF_EVEN".  This is the mode used by the builtin round() function: (...)

Right, round(float) and round(decimal.Decimal) uses the ROUND_HALF_EVEN rounding method.

On Python < 3.3, datetime.datetime.fromtimestamp(float) doesn't use exactly ROUND_HALF_EVEN, but it looks more to "round half away from zero" (the decimal module doesn't seem to support this exact rounding method).

The difference between ROUND_HALF_EVEN and "round half away from zero" is subtle. The two rounding methods only return a different result on the following case:

   divmod(t + us * 1e-6, 1.0)[1] * 1e6 == 0.5

where t and us are integers (t is a number of seconds created by mktime() and us is a number of microseconds in [0; 999999]).

I don't think that the case can occur. I failed to find such case for various values of t between 0 and 2**40, and us=0 or us=1. 1e-6 (10^-6 = 0.000001) cannot be represented exactly in base 2 (IEEE 754).

--

To move forward, we should agree on which rounding method datetime.datetime.fromtimestamp() should use, implement it in pytime.c (add a constant in pytime.h, implement it in pytime.c, and then write unit tests in test_time.py), and then use it in datetime.datetime.fromtimestamp().

IMHO we should only modify the rounding method used by datetime.datetime.fromtimestamp() and datetime.datetime.utcfromtimestamp(), other functions use the "right" rounding method.