From 69cc487df42d9064a74551ae26a8c115dade3e3a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 8 Sep 2015 23:58:54 +0200 Subject: Revert change 0eb8c182131e: """Issue #23517: datetime.timedelta constructor now rounds microseconds to nearest with ties going away from zero (ROUND_HALF_UP), as Python 2 and Python older than 3.3, instead of rounding to nearest with ties going to nearest even integer (ROUND_HALF_EVEN).""" datetime.timedelta uses rounding mode ROUND_HALF_EVEN again. --- Lib/datetime.py | 4 ++-- Lib/test/datetimetester.py | 18 ++++++++++++------ Misc/NEWS | 7 +------ Modules/_datetimemodule.c | 22 +++++++++++++++++++++- 4 files changed, 36 insertions(+), 15 deletions(-) diff --git a/Lib/datetime.py b/Lib/datetime.py index 6b2ac33..3c25ef8 100644 --- a/Lib/datetime.py +++ b/Lib/datetime.py @@ -407,7 +407,7 @@ class timedelta: # secondsfrac isn't referenced again if isinstance(microseconds, float): - microseconds = _round_half_up(microseconds + usdouble) + microseconds = round(microseconds + usdouble) seconds, microseconds = divmod(microseconds, 1000000) days, seconds = divmod(seconds, 24*3600) d += days @@ -418,7 +418,7 @@ class timedelta: days, seconds = divmod(seconds, 24*3600) d += days s += seconds - microseconds = _round_half_up(microseconds + usdouble) + microseconds = round(microseconds + usdouble) assert isinstance(s, int) assert isinstance(microseconds, int) assert abs(s) <= 3 * 24 * 3600 diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index f23add3..d87b106 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -663,14 +663,16 @@ class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase): # 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.5/1000), td(microseconds=0)) + eq(td(milliseconds=-0.5/1000), td(microseconds=-0)) 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)) - eq(td(seconds=1/2**7), td(microseconds=7813)) - eq(td(seconds=-1/2**7), td(microseconds=-7813)) + eq(td(milliseconds=1.5/1000), td(microseconds=2)) + eq(td(milliseconds=-1.5/1000), td(microseconds=-2)) + eq(td(seconds=0.5/10**6), td(microseconds=0)) + eq(td(seconds=-0.5/10**6), td(microseconds=-0)) + eq(td(seconds=1/2**7), td(microseconds=7812)) + eq(td(seconds=-1/2**7), td(microseconds=-7812)) # Rounding due to contributions from more than one field. us_per_hour = 3600e6 @@ -683,6 +685,10 @@ class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase): 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 --git a/Misc/NEWS b/Misc/NEWS index c066a73..837288e 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -17,18 +17,13 @@ Core and Builtins Library ------- -- Issue #22241: timezone.utc name is now plain 'UTC', not 'UTC-00:00'. +- Issue #22241: timezone.utc name is now plain 'UTC', not 'UTC-00:00'. - Issue #23517: fromtimestamp() and utcfromtimestamp() methods of datetime.datetime now round microseconds to nearest with ties going away from zero (ROUND_HALF_UP), as Python 2 and Python older than 3.3, instead of rounding towards -Infinity (ROUND_FLOOR). -- Issue #23517: datetime.timedelta constructor now rounds microseconds to - nearest with ties going away from zero (ROUND_HALF_UP), as Python 2 and - Python older than 3.3, instead of rounding to nearest with ties going to - nearest even integer (ROUND_HALF_EVEN). - - Issue #23552: Timeit now warns when there is substantial (4x) variance between best and worst times. Patch from Serhiy Storchaka. diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index ea2bae6..24e83d3 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -2149,9 +2149,29 @@ delta_new(PyTypeObject *type, PyObject *args, PyObject *kw) 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 = _PyTime_RoundHalfUp(leftover_us); + 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; + } temp = PyLong_FromLong((long)whole_us); -- cgit v0.12