diff --git a/Include/pytime.h b/Include/pytime.h index 41fb80607db..54a0cc4db8e 100644 --- a/Include/pytime.h +++ b/Include/pytime.h @@ -31,9 +31,9 @@ typedef enum { /* Round towards infinity (+inf). For example, used for timeout to wait "at least" N seconds. */ _PyTime_ROUND_CEILING=1, - /* Round to nearest with ties going away from zero. + /* Round to nearest with ties going to nearest even integer. For example, used to round from a Python float. */ - _PyTime_ROUND_HALF_UP + _PyTime_ROUND_HALF_EVEN } _PyTime_round_t; /* Convert a time_t to a PyLong. */ @@ -44,8 +44,9 @@ PyAPI_FUNC(PyObject *) _PyLong_FromTime_t( PyAPI_FUNC(time_t) _PyLong_AsTime_t( PyObject *obj); -/* Round to nearest with ties going away from zero (_PyTime_ROUND_HALF_UP). */ -PyAPI_FUNC(double) _PyTime_RoundHalfUp( +/* Round to nearest with ties going to nearest even integer + (_PyTime_ROUND_HALF_EVEN) */ +PyAPI_FUNC(double) _PyTime_RoundHalfEven( double x); /* Convert a number of seconds, int or float, to time_t. */ diff --git a/Lib/datetime.py b/Lib/datetime.py index 3c25ef84c63..3f29bc4b7a0 100644 --- a/Lib/datetime.py +++ b/Lib/datetime.py @@ -1380,7 +1380,7 @@ class datetime(date): A timezone info object may be passed in as well. """ frac, t = _math.modf(t) - us = _round_half_up(frac * 1e6) + us = round(frac * 1e6) if us >= 1000000: t += 1 us -= 1000000 diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index d87b106cffd..467fbe2f457 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -1874,7 +1874,7 @@ class TestDateTime(TestDate): self.assertEqual(t, zero) t = fts(-1/2**7) self.assertEqual(t.second, 59) - self.assertEqual(t.microsecond, 992187) + self.assertEqual(t.microsecond, 992188) t = fts(1e-7) self.assertEqual(t, zero) @@ -1888,7 +1888,7 @@ class TestDateTime(TestDate): self.assertEqual(t.microsecond, 0) t = fts(1/2**7) self.assertEqual(t.second, 0) - self.assertEqual(t.microsecond, 7813) + self.assertEqual(t.microsecond, 7812) def test_insane_fromtimestamp(self): # It's possible that some platform maps time_t to double, diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py index d68dc4f1c61..493b197ba89 100644 --- a/Lib/test/test_time.py +++ b/Lib/test/test_time.py @@ -30,11 +30,11 @@ class _PyTime(enum.IntEnum): ROUND_FLOOR = 0 # Round towards infinity (+inf) ROUND_CEILING = 1 - # Round to nearest with ties going away from zero - ROUND_HALF_UP = 2 + # Round to nearest with ties going to nearest even integer + ROUND_HALF_EVEN = 2 ALL_ROUNDING_METHODS = (_PyTime.ROUND_FLOOR, _PyTime.ROUND_CEILING, - _PyTime.ROUND_HALF_UP) + _PyTime.ROUND_HALF_EVEN) class TimeTestCase(unittest.TestCase): @@ -639,27 +639,26 @@ class TestPytime(unittest.TestCase): # Conversion giving different results depending on the rounding method FLOOR = _PyTime.ROUND_FLOOR CEILING = _PyTime.ROUND_CEILING - HALF_UP = _PyTime.ROUND_HALF_UP - for obj, time_t, rnd in ( + HALF_EVEN = _PyTime.ROUND_HALF_EVEN + for obj, seconds, rnd in ( (-1.9, -2, FLOOR), (-1.9, -1, CEILING), - (-1.9, -2, HALF_UP), + (-1.9, -2, HALF_EVEN), (1.9, 1, FLOOR), (1.9, 2, CEILING), - (1.9, 2, HALF_UP), + (1.9, 2, HALF_EVEN), - # half up - (-0.999, -1, HALF_UP), - (-0.510, -1, HALF_UP), - (-0.500, -1, HALF_UP), - (-0.490, 0, HALF_UP), - ( 0.490, 0, HALF_UP), - ( 0.500, 1, HALF_UP), - ( 0.510, 1, HALF_UP), - ( 0.999, 1, HALF_UP), + # half even + (-1.5, -2, HALF_EVEN), + (-0.9, -1, HALF_EVEN), + (-0.5, 0, HALF_EVEN), + ( 0.5, 0, HALF_EVEN), + ( 0.9, 1, HALF_EVEN), + ( 1.5, 2, HALF_EVEN), ): - self.assertEqual(pytime_object_to_time_t(obj, rnd), time_t) + with self.subTest(obj=obj, round=rnd, seconds=seconds): + self.assertEqual(pytime_object_to_time_t(obj, rnd), seconds) # Test OverflowError rnd = _PyTime.ROUND_FLOOR @@ -691,15 +690,15 @@ class TestPytime(unittest.TestCase): # Conversion giving different results depending on the rounding method FLOOR = _PyTime.ROUND_FLOOR CEILING = _PyTime.ROUND_CEILING - HALF_UP = _PyTime.ROUND_HALF_UP + HALF_EVEN = _PyTime.ROUND_HALF_EVEN for obj, timespec, rnd in ( # Round towards minus infinity (-inf) (-1e-10, (0, 0), CEILING), (-1e-10, (-1, 999999999), FLOOR), - (-1e-10, (0, 0), HALF_UP), + (-1e-10, (0, 0), HALF_EVEN), (1e-10, (0, 0), FLOOR), (1e-10, (0, 1), CEILING), - (1e-10, (0, 0), HALF_UP), + (1e-10, (0, 0), HALF_EVEN), (0.9999999999, (0, 999999999), FLOOR), (0.9999999999, (1, 0), CEILING), @@ -714,15 +713,13 @@ class TestPytime(unittest.TestCase): (-1.1234567890, (-2, 876543211), CEILING), (-1.1234567891, (-2, 876543211), CEILING), - # half up - (-0.6e-9, (-1, 999999999), HALF_UP), - # skipped, 0.5e-6 is inexact in base 2 - #(-0.5e-9, (-1, 999999999), HALF_UP), - (-0.4e-9, (0, 0), HALF_UP), - - (0.4e-9, (0, 0), HALF_UP), - (0.5e-9, (0, 1), HALF_UP), - (0.6e-9, (0, 1), HALF_UP), + # half even + (-1.5e-9, (-1, 999999998), HALF_EVEN), + (-0.9e-9, (-1, 999999999), HALF_EVEN), + (-0.5e-9, (0, 0), HALF_EVEN), + (0.5e-9, (0, 0), HALF_EVEN), + (0.9e-9, (0, 1), HALF_EVEN), + (1.5e-9, (0, 2), HALF_EVEN), ): with self.subTest(obj=obj, round=rnd, timespec=timespec): self.assertEqual(pytime_object_to_timespec(obj, rnd), timespec) @@ -823,10 +820,10 @@ class TestPyTime_t(unittest.TestCase): (-7.0, -7 * SEC_TO_NS), # nanosecond are kept for value <= 2^23 seconds, - # except 2**23-1e-9 with HALF_UP (2**22 - 1e-9, 4194303999999999), (2**22, 4194304000000000), (2**22 + 1e-9, 4194304000000001), + (2**23 - 1e-9, 8388607999999999), (2**23, 8388608000000000), # start loosing precision for value > 2^23 seconds @@ -859,38 +856,31 @@ class TestPyTime_t(unittest.TestCase): # Conversion giving different results depending on the rounding method FLOOR = _PyTime.ROUND_FLOOR CEILING = _PyTime.ROUND_CEILING - HALF_UP = _PyTime.ROUND_HALF_UP + HALF_EVEN = _PyTime.ROUND_HALF_EVEN for obj, ts, rnd in ( # close to zero ( 1e-10, 0, FLOOR), ( 1e-10, 1, CEILING), - ( 1e-10, 0, HALF_UP), + ( 1e-10, 0, HALF_EVEN), (-1e-10, -1, FLOOR), (-1e-10, 0, CEILING), - (-1e-10, 0, HALF_UP), + (-1e-10, 0, HALF_EVEN), # test rounding of the last nanosecond ( 1.1234567899, 1123456789, FLOOR), ( 1.1234567899, 1123456790, CEILING), - ( 1.1234567899, 1123456790, HALF_UP), + ( 1.1234567899, 1123456790, HALF_EVEN), (-1.1234567899, -1123456790, FLOOR), (-1.1234567899, -1123456789, CEILING), - (-1.1234567899, -1123456790, HALF_UP), + (-1.1234567899, -1123456790, HALF_EVEN), # close to 1 second ( 0.9999999999, 999999999, FLOOR), ( 0.9999999999, 1000000000, CEILING), - ( 0.9999999999, 1000000000, HALF_UP), + ( 0.9999999999, 1000000000, HALF_EVEN), (-0.9999999999, -1000000000, FLOOR), (-0.9999999999, -999999999, CEILING), - (-0.9999999999, -1000000000, HALF_UP), - - # close to 2^23 seconds - (2**23 - 1e-9, 8388607999999999, FLOOR), - (2**23 - 1e-9, 8388607999999999, CEILING), - # Issue #23517: skip HALF_UP test because the result is different - # depending on the FPU and how the compiler optimize the code :-/ - #(2**23 - 1e-9, 8388608000000000, HALF_UP), + (-0.9999999999, -1000000000, HALF_EVEN), ): with self.subTest(obj=obj, round=rnd, timestamp=ts): self.assertEqual(PyTime_FromSecondsObject(obj, rnd), ts) @@ -958,33 +948,23 @@ class TestPyTime_t(unittest.TestCase): FLOOR = _PyTime.ROUND_FLOOR CEILING = _PyTime.ROUND_CEILING - HALF_UP = _PyTime.ROUND_HALF_UP + HALF_EVEN = _PyTime.ROUND_HALF_EVEN for ns, tv, rnd in ( # nanoseconds (1, (0, 0), FLOOR), (1, (0, 1), CEILING), - (1, (0, 0), HALF_UP), + (1, (0, 0), HALF_EVEN), (-1, (-1, 999999), FLOOR), (-1, (0, 0), CEILING), - (-1, (0, 0), HALF_UP), + (-1, (0, 0), HALF_EVEN), - # seconds + nanoseconds - (1234567001, (1, 234567), FLOOR), - (1234567001, (1, 234568), CEILING), - (1234567001, (1, 234567), HALF_UP), - (-1234567001, (-2, 765432), FLOOR), - (-1234567001, (-2, 765433), CEILING), - (-1234567001, (-2, 765433), HALF_UP), - - # half up - (499, (0, 0), HALF_UP), - (500, (0, 1), HALF_UP), - (501, (0, 1), HALF_UP), - (999, (0, 1), HALF_UP), - (-499, (0, 0), HALF_UP), - (-500, (0, 0), HALF_UP), - (-501, (-1, 999999), HALF_UP), - (-999, (-1, 999999), HALF_UP), + # half even + (-1500, (-1, 999998), HALF_EVEN), + (-999, (-1, 999999), HALF_EVEN), + (-500, (0, 0), HALF_EVEN), + (500, (0, 0), HALF_EVEN), + (999, (0, 1), HALF_EVEN), + (1500, (0, 2), HALF_EVEN), ): with self.subTest(nanoseconds=ns, timeval=tv, round=rnd): self.assertEqual(PyTime_AsTimeval(ns, rnd), tv) @@ -1027,33 +1007,31 @@ class TestPyTime_t(unittest.TestCase): FLOOR = _PyTime.ROUND_FLOOR CEILING = _PyTime.ROUND_CEILING - HALF_UP = _PyTime.ROUND_HALF_UP + HALF_EVEN = _PyTime.ROUND_HALF_EVEN for ns, ms, rnd in ( # nanoseconds (1, 0, FLOOR), (1, 1, CEILING), - (1, 0, HALF_UP), - (-1, 0, FLOOR), - (-1, -1, CEILING), - (-1, 0, HALF_UP), + (1, 0, HALF_EVEN), + (-1, -1, FLOOR), + (-1, 0, CEILING), + (-1, 0, HALF_EVEN), # seconds + nanoseconds (1234 * MS_TO_NS + 1, 1234, FLOOR), (1234 * MS_TO_NS + 1, 1235, CEILING), - (1234 * MS_TO_NS + 1, 1234, HALF_UP), - (-1234 * MS_TO_NS - 1, -1234, FLOOR), - (-1234 * MS_TO_NS - 1, -1235, CEILING), - (-1234 * MS_TO_NS - 1, -1234, HALF_UP), + (1234 * MS_TO_NS + 1, 1234, HALF_EVEN), + (-1234 * MS_TO_NS - 1, -1235, FLOOR), + (-1234 * MS_TO_NS - 1, -1234, CEILING), + (-1234 * MS_TO_NS - 1, -1234, HALF_EVEN), # half up - (499999, 0, HALF_UP), - (499999, 0, HALF_UP), - (500000, 1, HALF_UP), - (999999, 1, HALF_UP), - (-499999, 0, HALF_UP), - (-500000, -1, HALF_UP), - (-500001, -1, HALF_UP), - (-999999, -1, HALF_UP), + (-1500000, -2, HALF_EVEN), + (-999999, -1, HALF_EVEN), + (-500000, 0, HALF_EVEN), + (500000, 0, HALF_EVEN), + (999999, 1, HALF_EVEN), + (1500000, 2, HALF_EVEN), ): with self.subTest(nanoseconds=ns, milliseconds=ms, round=rnd): self.assertEqual(PyTime_AsMilliseconds(ns, rnd), ms) @@ -1079,31 +1057,31 @@ class TestPyTime_t(unittest.TestCase): FLOOR = _PyTime.ROUND_FLOOR CEILING = _PyTime.ROUND_CEILING - HALF_UP = _PyTime.ROUND_HALF_UP + HALF_EVEN = _PyTime.ROUND_HALF_EVEN for ns, ms, rnd in ( # nanoseconds (1, 0, FLOOR), (1, 1, CEILING), - (1, 0, HALF_UP), - (-1, 0, FLOOR), - (-1, -1, CEILING), - (-1, 0, HALF_UP), + (1, 0, HALF_EVEN), + (-1, -1, FLOOR), + (-1, 0, CEILING), + (-1, 0, HALF_EVEN), # seconds + nanoseconds (1234 * US_TO_NS + 1, 1234, FLOOR), (1234 * US_TO_NS + 1, 1235, CEILING), - (1234 * US_TO_NS + 1, 1234, HALF_UP), - (-1234 * US_TO_NS - 1, -1234, FLOOR), - (-1234 * US_TO_NS - 1, -1235, CEILING), - (-1234 * US_TO_NS - 1, -1234, HALF_UP), + (1234 * US_TO_NS + 1, 1234, HALF_EVEN), + (-1234 * US_TO_NS - 1, -1235, FLOOR), + (-1234 * US_TO_NS - 1, -1234, CEILING), + (-1234 * US_TO_NS - 1, -1234, HALF_EVEN), # half up - (1499, 1, HALF_UP), - (1500, 2, HALF_UP), - (1501, 2, HALF_UP), - (-1499, -1, HALF_UP), - (-1500, -2, HALF_UP), - (-1501, -2, HALF_UP), + (-1500, -2, HALF_EVEN), + (-999, -1, HALF_EVEN), + (-500, 0, HALF_EVEN), + (500, 0, HALF_EVEN), + (999, 1, HALF_EVEN), + (1500, 2, HALF_EVEN), ): with self.subTest(nanoseconds=ns, milliseconds=ms, round=rnd): self.assertEqual(PyTime_AsMicroseconds(ns, rnd), ms) @@ -1142,23 +1120,23 @@ class TestOldPyTime(unittest.TestCase): # Conversion giving different results depending on the rounding method FLOOR = _PyTime.ROUND_FLOOR CEILING = _PyTime.ROUND_CEILING - HALF_UP = _PyTime.ROUND_HALF_UP + HALF_EVEN = _PyTime.ROUND_HALF_EVEN for obj, time_t, rnd in ( (-1.9, -2, FLOOR), - (-1.9, -2, HALF_UP), + (-1.9, -2, HALF_EVEN), (-1.9, -1, CEILING), (1.9, 1, FLOOR), - (1.9, 2, HALF_UP), + (1.9, 2, HALF_EVEN), (1.9, 2, CEILING), - (-0.6, -1, HALF_UP), - (-0.5, -1, HALF_UP), - (-0.4, 0, HALF_UP), - - (0.4, 0, HALF_UP), - (0.5, 1, HALF_UP), - (0.6, 1, HALF_UP), + # half even + (-1.5, -2, HALF_EVEN), + (-0.9, -1, HALF_EVEN), + (-0.5, 0, HALF_EVEN), + ( 0.5, 0, HALF_EVEN), + ( 0.9, 1, HALF_EVEN), + ( 1.5, 2, HALF_EVEN), ): self.assertEqual(pytime_object_to_time_t(obj, rnd), time_t) @@ -1192,29 +1170,27 @@ class TestOldPyTime(unittest.TestCase): # Conversion giving different results depending on the rounding method FLOOR = _PyTime.ROUND_FLOOR CEILING = _PyTime.ROUND_CEILING - HALF_UP = _PyTime.ROUND_HALF_UP + HALF_EVEN = _PyTime.ROUND_HALF_EVEN for obj, timeval, rnd in ( (-1e-7, (-1, 999999), FLOOR), (-1e-7, (0, 0), CEILING), - (-1e-7, (0, 0), HALF_UP), + (-1e-7, (0, 0), HALF_EVEN), (1e-7, (0, 0), FLOOR), (1e-7, (0, 1), CEILING), - (1e-7, (0, 0), HALF_UP), + (1e-7, (0, 0), HALF_EVEN), (0.9999999, (0, 999999), FLOOR), (0.9999999, (1, 0), CEILING), - (0.9999999, (1, 0), HALF_UP), + (0.9999999, (1, 0), HALF_EVEN), - (-0.6e-6, (-1, 999999), HALF_UP), - # skipped, -0.5e-6 is inexact in base 2 - #(-0.5e-6, (-1, 999999), HALF_UP), - (-0.4e-6, (0, 0), HALF_UP), - - (0.4e-6, (0, 0), HALF_UP), - # skipped, 0.5e-6 is inexact in base 2 - #(0.5e-6, (0, 1), HALF_UP), - (0.6e-6, (0, 1), HALF_UP), + # half even + (-1.5e-6, (-1, 999998), HALF_EVEN), + (-0.9e-6, (-1, 999999), HALF_EVEN), + (-0.5e-6, (0, 0), HALF_EVEN), + (0.5e-6, (0, 0), HALF_EVEN), + (0.9e-6, (0, 1), HALF_EVEN), + (1.5e-6, (0, 2), HALF_EVEN), ): with self.subTest(obj=obj, round=rnd, timeval=timeval): self.assertEqual(pytime_object_to_timeval(obj, rnd), timeval) @@ -1248,28 +1224,27 @@ class TestOldPyTime(unittest.TestCase): # Conversion giving different results depending on the rounding method FLOOR = _PyTime.ROUND_FLOOR CEILING = _PyTime.ROUND_CEILING - HALF_UP = _PyTime.ROUND_HALF_UP + HALF_EVEN = _PyTime.ROUND_HALF_EVEN for obj, timespec, rnd in ( (-1e-10, (-1, 999999999), FLOOR), (-1e-10, (0, 0), CEILING), - (-1e-10, (0, 0), HALF_UP), + (-1e-10, (0, 0), HALF_EVEN), (1e-10, (0, 0), FLOOR), (1e-10, (0, 1), CEILING), - (1e-10, (0, 0), HALF_UP), + (1e-10, (0, 0), HALF_EVEN), (0.9999999999, (0, 999999999), FLOOR), (0.9999999999, (1, 0), CEILING), - (0.9999999999, (1, 0), HALF_UP), + (0.9999999999, (1, 0), HALF_EVEN), - (-0.6e-9, (-1, 999999999), HALF_UP), - # skipped, 0.5e-6 is inexact in base 2 - #(-0.5e-9, (-1, 999999999), HALF_UP), - (-0.4e-9, (0, 0), HALF_UP), - - (0.4e-9, (0, 0), HALF_UP), - (0.5e-9, (0, 1), HALF_UP), - (0.6e-9, (0, 1), HALF_UP), + # half even + (-1.5e-9, (-1, 999999998), HALF_EVEN), + (-0.9e-9, (-1, 999999999), HALF_EVEN), + (-0.5e-9, (0, 0), HALF_EVEN), + (0.5e-9, (0, 0), HALF_EVEN), + (0.9e-9, (0, 1), HALF_EVEN), + (1.5e-9, (0, 2), HALF_EVEN), ): with self.subTest(obj=obj, round=rnd, timespec=timespec): self.assertEqual(pytime_object_to_timespec(obj, rnd), timespec) diff --git a/Misc/NEWS b/Misc/NEWS index 837288e0f79..c2f2e87e9b2 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -20,9 +20,9 @@ Library - 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). + datetime.datetime now round microseconds to nearest with ties going to + nearest even integer (ROUND_HALF_EVEN), as round(float), instead of rounding + towards -Infinity (ROUND_FLOOR). - 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 24e83d3d291..bbc51c6a68a 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -4103,7 +4103,7 @@ datetime_from_timestamp(PyObject *cls, TM_FUNC f, PyObject *timestamp, long us; if (_PyTime_ObjectToTimeval(timestamp, - &timet, &us, _PyTime_ROUND_HALF_UP) == -1) + &timet, &us, _PyTime_ROUND_HALF_EVEN) == -1) return NULL; return datetime_from_timet_and_us(cls, f, timet, (int)us, tzinfo); diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index c38d9ae6346..fed3286ac19 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -2648,7 +2648,7 @@ check_time_rounding(int round) { if (round != _PyTime_ROUND_FLOOR && round != _PyTime_ROUND_CEILING - && round != _PyTime_ROUND_HALF_UP) { + && round != _PyTime_ROUND_HALF_EVEN) { PyErr_SetString(PyExc_ValueError, "invalid rounding"); return -1; } diff --git a/Python/pytime.c b/Python/pytime.c index 036447365cd..9f7ee2d0ed8 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -61,18 +61,15 @@ _PyLong_FromTime_t(time_t t) } double -_PyTime_RoundHalfUp(double x) +_PyTime_RoundHalfEven(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; + double rounded = round(x); + if (fabs(x-rounded) == 0.5) + /* halfway case: round to even */ + rounded = 2.0*round(x/2.0); + return rounded; } - static int _PyTime_DoubleToDenominator(double d, time_t *sec, long *numerator, double denominator, _PyTime_round_t round) @@ -84,8 +81,8 @@ _PyTime_DoubleToDenominator(double d, time_t *sec, long *numerator, floatpart = modf(d, &intpart); floatpart *= denominator; - if (round == _PyTime_ROUND_HALF_UP) - floatpart = _PyTime_RoundHalfUp(floatpart); + if (round == _PyTime_ROUND_HALF_EVEN) + floatpart = _PyTime_RoundHalfEven(floatpart); else if (round == _PyTime_ROUND_CEILING) floatpart = ceil(floatpart); else @@ -140,8 +137,8 @@ _PyTime_ObjectToTime_t(PyObject *obj, time_t *sec, _PyTime_round_t round) volatile double d; d = PyFloat_AsDouble(obj); - if (round == _PyTime_ROUND_HALF_UP) - d = _PyTime_RoundHalfUp(d); + if (round == _PyTime_ROUND_HALF_EVEN) + d = _PyTime_RoundHalfEven(d); else if (round == _PyTime_ROUND_CEILING) d = ceil(d); else @@ -266,8 +263,8 @@ _PyTime_FromFloatObject(_PyTime_t *t, double value, _PyTime_round_t round, d = value; d *= to_nanoseconds; - if (round == _PyTime_ROUND_HALF_UP) - d = _PyTime_RoundHalfUp(d); + if (round == _PyTime_ROUND_HALF_EVEN) + d = _PyTime_RoundHalfEven(d); else if (round == _PyTime_ROUND_CEILING) d = ceil(d); else @@ -351,14 +348,16 @@ _PyTime_AsNanosecondsObject(_PyTime_t t) } static _PyTime_t -_PyTime_Divide(_PyTime_t t, _PyTime_t k, _PyTime_round_t round) +_PyTime_Divide(const _PyTime_t t, const _PyTime_t k, + const _PyTime_round_t round) { assert(k > 1); - if (round == _PyTime_ROUND_HALF_UP) { - _PyTime_t x, r; + if (round == _PyTime_ROUND_HALF_EVEN) { + _PyTime_t x, r, abs_r; x = t / k; r = t % k; - if (Py_ABS(r) >= k / 2) { + abs_r = Py_ABS(r); + if (abs_r > k / 2 || (abs_r == k / 2 && (Py_ABS(x) & 1))) { if (t >= 0) x++; else @@ -369,11 +368,15 @@ _PyTime_Divide(_PyTime_t t, _PyTime_t k, _PyTime_round_t round) else if (round == _PyTime_ROUND_CEILING) { if (t >= 0) return (t + k - 1) / k; + else + return t / k; + } + else { + if (t >= 0) + return t / k; else return (t - (k - 1)) / k; } - else - return t / k; } _PyTime_t @@ -392,17 +395,12 @@ static int _PyTime_AsTimeval_impl(_PyTime_t t, struct timeval *tv, _PyTime_round_t round, int raise) { - const long k = US_TO_NS; _PyTime_t secs, ns; int res = 0; int usec; secs = t / SEC_TO_NS; ns = t % SEC_TO_NS; - if (ns < 0) { - ns += SEC_TO_NS; - secs -= 1; - } #ifdef MS_WINDOWS /* On Windows, timeval.tv_sec is a long (32 bit), @@ -427,23 +425,12 @@ _PyTime_AsTimeval_impl(_PyTime_t t, struct timeval *tv, _PyTime_round_t round, res = -1; #endif - if (round == _PyTime_ROUND_HALF_UP) { - _PyTime_t r; - usec = (int)(ns / k); - r = ns % k; - if (Py_ABS(r) >= k / 2) { - if (ns >= 0) - usec++; - else - usec--; - } + usec = (int)_PyTime_Divide(ns, US_TO_NS, round); + if (usec < 0) { + usec += SEC_TO_US; + tv->tv_sec -= 1; } - else if (round == _PyTime_ROUND_CEILING) - usec = (int)((ns + k - 1) / k); - else - usec = (int)(ns / k); - - if (usec >= SEC_TO_US) { + else if (usec >= SEC_TO_US) { usec -= SEC_TO_US; tv->tv_sec += 1; }