diff -r 29fa1d33e421 Include/pytime.h --- a/Include/pytime.h Thu Sep 03 15:42:26 2015 +0200 +++ b/Include/pytime.h Fri Sep 04 10:07:02 2015 +0200 @@ -61,6 +61,10 @@ typedef enum { _PyTime_ROUND_UP } _PyTime_round_t; +/* Round to nearest with ties going away from zero (_PyTime_ROUND_HALF_UP). */ +PyAPI_FUNC(double) _PyTime_RoundHalfUp( + double x); + /* Convert a number of seconds, int or float, to time_t. */ PyAPI_FUNC(int) _PyTime_ObjectToTime_t( PyObject *obj, diff -r 29fa1d33e421 Lib/datetime.py --- a/Lib/datetime.py Thu Sep 03 15:42:26 2015 +0200 +++ b/Lib/datetime.py Fri Sep 04 10:07:02 2015 +0200 @@ -299,6 +299,14 @@ def _divide_and_round(a, b): return q +def _round_half_up(x): + """Round to nearest with ties going away from zero.""" + if x >= 0.0: + return _math.floor(x + 0.5) + else: + return _math.ceil(x - 0.5) + + class timedelta: """Represent the difference between two datetime objects. @@ -382,38 +390,26 @@ class timedelta: # secondsfrac isn't referenced again if isinstance(microseconds, float): - microseconds += usdouble - microseconds = round(microseconds, 0) - seconds, microseconds = divmod(microseconds, 1e6) - assert microseconds == int(microseconds) - assert seconds == int(seconds) - days, seconds = divmod(seconds, 24.*3600.) - assert days == int(days) - assert seconds == int(seconds) - d += int(days) - s += int(seconds) # can't overflow - assert isinstance(s, int) - assert abs(s) <= 3 * 24 * 3600 - else: + microseconds = _round_half_up(microseconds + usdouble) seconds, microseconds = divmod(microseconds, 1000000) days, seconds = divmod(seconds, 24*3600) d += days - s += int(seconds) # can't overflow - assert isinstance(s, int) - assert abs(s) <= 3 * 24 * 3600 - microseconds = float(microseconds) - microseconds += usdouble - microseconds = round(microseconds, 0) + s += seconds + else: + microseconds = int(microseconds) + seconds, microseconds = divmod(microseconds, 1000000) + days, seconds = divmod(seconds, 24*3600) + d += days + s += seconds + microseconds = _round_half_up(microseconds + usdouble) + assert isinstance(s, int) + assert isinstance(microseconds, int) assert abs(s) <= 3 * 24 * 3600 assert abs(microseconds) < 3.1e6 # Just a little bit of carrying possible for microseconds and seconds. - assert isinstance(microseconds, float) - assert int(microseconds) == microseconds - us = int(microseconds) - seconds, us = divmod(us, 1000000) - s += seconds # cant't overflow - assert isinstance(s, int) + seconds, us = divmod(microseconds, 1000000) + s += seconds days, s = divmod(s, 24*3600) d += days @@ -421,14 +417,13 @@ class timedelta: assert isinstance(s, int) and 0 <= s < 24*3600 assert isinstance(us, int) and 0 <= us < 1000000 + if abs(d) > 999999999: + raise OverflowError("timedelta # of days is too large: %d" % d) + self = object.__new__(cls) - self._days = d self._seconds = s self._microseconds = us - if abs(d) > 999999999: - raise OverflowError("timedelta # of days is too large: %d" % d) - return self def __repr__(self): diff -r 29fa1d33e421 Lib/test/datetimetester.py --- a/Lib/test/datetimetester.py Thu Sep 03 15:42:26 2015 +0200 +++ b/Lib/test/datetimetester.py Fri Sep 04 10:07:02 2015 +0200 @@ -650,24 +650,24 @@ class TestTimeDelta(HarmlessMixedCompari # Single-field rounding. eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0 + eq(td(milliseconds=0.5/1000), td(microseconds=1)) + eq(td(milliseconds=-0.5/1000), td(microseconds=-1)) eq(td(milliseconds=0.6/1000), td(microseconds=1)) eq(td(milliseconds=-0.6/1000), td(microseconds=-1)) + eq(td(seconds=0.5/10**6), td(microseconds=1)) + eq(td(seconds=-0.5/10**6), td(microseconds=-1)) # Rounding due to contributions from more than one field. us_per_hour = 3600e6 us_per_day = us_per_hour * 24 eq(td(days=.4/us_per_day), td(0)) eq(td(hours=.2/us_per_hour), td(0)) - eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1)) + eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1), td) eq(td(days=-.4/us_per_day), td(0)) eq(td(hours=-.2/us_per_hour), td(0)) eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1)) - # Test for a patch in Issue 8860 - eq(td(microseconds=0.5), 0.5*td(microseconds=1.0)) - eq(td(microseconds=0.5)//td.resolution, 0.5*td.resolution//td.resolution) - def test_massive_normalization(self): td = timedelta(microseconds=-1) self.assertEqual((td.days, td.seconds, td.microseconds), diff -r 29fa1d33e421 Modules/_datetimemodule.c --- a/Modules/_datetimemodule.c Thu Sep 03 15:42:26 2015 +0200 +++ b/Modules/_datetimemodule.c Fri Sep 04 10:07:02 2015 +0200 @@ -2143,29 +2143,9 @@ delta_new(PyTypeObject *type, PyObject * if (leftover_us) { /* Round to nearest whole # of us, and add into x. */ double whole_us = round(leftover_us); - int x_is_odd; PyObject *temp; - whole_us = round(leftover_us); - if (fabs(whole_us - leftover_us) == 0.5) { - /* We're exactly halfway between two integers. In order - * to do round-half-to-even, we must determine whether x - * is odd. Note that x is odd when it's last bit is 1. The - * code below uses bitwise and operation to check the last - * bit. */ - temp = PyNumber_And(x, one); /* temp <- x & 1 */ - if (temp == NULL) { - Py_DECREF(x); - goto Done; - } - x_is_odd = PyObject_IsTrue(temp); - Py_DECREF(temp); - if (x_is_odd == -1) { - Py_DECREF(x); - goto Done; - } - whole_us = 2.0 * round((leftover_us + x_is_odd) * 0.5) - x_is_odd; - } + whole_us = _PyTime_RoundHalfUp(leftover_us); temp = PyLong_FromLong((long)whole_us); diff -r 29fa1d33e421 Python/pytime.c --- a/Python/pytime.c Thu Sep 03 15:42:26 2015 +0200 +++ b/Python/pytime.c Fri Sep 04 10:07:02 2015 +0200 @@ -150,6 +150,18 @@ PyObject * #endif } +double +_PyTime_RoundHalfUp(double x) +{ + /* volatile avoids optimization changing how numbers are rounded */ + volatile double d = x; + if (d >= 0.0) + d = floor(d + 0.5); + else + d = ceil(d - 0.5); + return d; +} + static int _PyTime_ObjectToDenominator(PyObject *obj, time_t *sec, long *numerator, double denominator, _PyTime_round_t round)