[Python-checkins] cpython: Issue #23517: fromtimestamp() and utcfromtimestamp() methods of
victor.stinner
python-checkins at python.org
Wed Sep 9 01:04:29 CEST 2015
https://hg.python.org/cpython/rev/171d5590ebc3
changeset: 97782:171d5590ebc3
user: Victor Stinner <victor.stinner at gmail.com>
date: Wed Sep 09 01:02:23 2015 +0200
summary:
Issue #23517: fromtimestamp() and utcfromtimestamp() methods of
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).
pytime API: replace _PyTime_ROUND_HALF_UP with _PyTime_ROUND_HALF_EVEN. Fix
also _PyTime_Divide() for negative numbers.
_PyTime_AsTimeval_impl() now reuses _PyTime_Divide() instead of reimplementing
rounding modes.
files:
Include/pytime.h | 9 +-
Lib/datetime.py | 2 +-
Lib/test/datetimetester.py | 4 +-
Lib/test/test_time.py | 237 +++++++++++-------------
Misc/NEWS | 6 +-
Modules/_datetimemodule.c | 2 +-
Modules/_testcapimodule.c | 2 +-
Python/pytime.c | 71 +++----
8 files changed, 148 insertions(+), 185 deletions(-)
diff --git a/Include/pytime.h b/Include/pytime.h
--- a/Include/pytime.h
+++ b/Include/pytime.h
@@ -31,9 +31,9 @@
/* 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(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
--- a/Lib/datetime.py
+++ b/Lib/datetime.py
@@ -1380,7 +1380,7 @@
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
--- a/Lib/test/datetimetester.py
+++ b/Lib/test/datetimetester.py
@@ -1874,7 +1874,7 @@
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 @@
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
--- a/Lib/test/test_time.py
+++ b/Lib/test/test_time.py
@@ -30,11 +30,11 @@
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 @@
# 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 @@
# 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 @@
(-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 @@
(-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 @@
# 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 @@
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 @@
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 @@
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 @@
# 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 @@
# 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 @@
# 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
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -20,9 +20,9 @@
- 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
--- a/Modules/_datetimemodule.c
+++ b/Modules/_datetimemodule.c
@@ -4103,7 +4103,7 @@
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
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -2648,7 +2648,7 @@
{
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
--- a/Python/pytime.c
+++ b/Python/pytime.c
@@ -61,18 +61,15 @@
}
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 @@
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 @@
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 @@
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 @@
}
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
@@ -370,10 +369,14 @@
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 @@
_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 @@
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;
}
--
Repository URL: https://hg.python.org/cpython
More information about the Python-checkins
mailing list