Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 790d269

Browse files
committed
Fixes #8860: Round half-microseconds to even in the timedelta constructor.
(Original patch by Mark Dickinson.)
1 parent 5e5a823 commit 790d269

4 files changed

Lines changed: 42 additions & 22 deletions

File tree

Doc/library/datetime.rst

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -170,10 +170,12 @@ dates or times.
170170
* ``0 <= seconds < 3600*24`` (the number of seconds in one day)
171171
* ``-999999999 <= days <= 999999999``
172172

173-
If any argument is a float and there are fractional microseconds, the fractional
174-
microseconds left over from all arguments are combined and their sum is rounded
175-
to the nearest microsecond. If no argument is a float, the conversion and
176-
normalization processes are exact (no information is lost).
173+
If any argument is a float and there are fractional microseconds,
174+
the fractional microseconds left over from all arguments are
175+
combined and their sum is rounded to the nearest microsecond using
176+
round-half-to-even tiebreaker. If no argument is a float, the
177+
conversion and normalization processes are exact (no information is
178+
lost).
177179

178180
If the normalized value of days lies outside the indicated range,
179181
:exc:`OverflowError` is raised.

Lib/test/datetimetester.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,10 @@ def test_microsecond_rounding(self):
619619
eq(td(hours=-.2/us_per_hour), td(0))
620620
eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
621621

622+
# Test for a patch in Issue 8860
623+
eq(td(microseconds=0.5), 0.5*td(microseconds=1.0))
624+
eq(td(microseconds=0.5)//td.resolution, 0.5*td.resolution//td.resolution)
625+
622626
def test_massive_normalization(self):
623627
td = timedelta(microseconds=-1)
624628
self.assertEqual((td.days, td.seconds, td.microseconds),

Misc/NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Core and Builtins
1313
Library
1414
-------
1515

16+
- Issue 8860: Fixed rounding in timedelta constructor.
1617

1718
What's New in Python 3.4.0 Alpha 1?
1819
===================================

Modules/_datetimemodule.c

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -140,19 +140,6 @@ divmod(int x, int y, int *r)
140140
return quo;
141141
}
142142

143-
/* Round a double to the nearest long. |x| must be small enough to fit
144-
* in a C long; this is not checked.
145-
*/
146-
static long
147-
round_to_long(double x)
148-
{
149-
if (x >= 0.0)
150-
x = floor(x + 0.5);
151-
else
152-
x = ceil(x - 0.5);
153-
return (long)x;
154-
}
155-
156143
/* Nearest integer to m / n for integers m and n. Half-integer results
157144
* are rounded to even.
158145
*/
@@ -1397,7 +1384,7 @@ cmperror(PyObject *a, PyObject *b)
13971384
*/
13981385

13991386
/* Conversion factors. */
1400-
static PyObject *us_per_us = NULL; /* 1 */
1387+
static PyObject *one = NULL; /* 1 */
14011388
static PyObject *us_per_ms = NULL; /* 1000 */
14021389
static PyObject *us_per_second = NULL; /* 1000000 */
14031390
static PyObject *us_per_minute = NULL; /* 1e6 * 60 as Python int */
@@ -2119,7 +2106,7 @@ delta_new(PyTypeObject *type, PyObject *args, PyObject *kw)
21192106
goto Done
21202107

21212108
if (us) {
2122-
y = accum("microseconds", x, us, us_per_us, &leftover_us);
2109+
y = accum("microseconds", x, us, one, &leftover_us);
21232110
CLEANUP;
21242111
}
21252112
if (ms) {
@@ -2148,7 +2135,33 @@ delta_new(PyTypeObject *type, PyObject *args, PyObject *kw)
21482135
}
21492136
if (leftover_us) {
21502137
/* Round to nearest whole # of us, and add into x. */
2151-
PyObject *temp = PyLong_FromLong(round_to_long(leftover_us));
2138+
double whole_us = round(leftover_us);
2139+
int x_is_odd;
2140+
PyObject *temp;
2141+
2142+
whole_us = round(leftover_us);
2143+
if (fabs(whole_us - leftover_us) == 0.5) {
2144+
/* We're exactly halfway between two integers. In order
2145+
* to do round-half-to-even, we must determine whether x
2146+
* is odd. Note that x is odd when it's last bit is 1. The
2147+
* code below uses bitwise and operation to check the last
2148+
* bit. */
2149+
temp = PyNumber_And(x, one); /* temp <- x & 1 */
2150+
if (temp == NULL) {
2151+
Py_DECREF(x);
2152+
goto Done;
2153+
}
2154+
x_is_odd = PyObject_IsTrue(temp);
2155+
Py_DECREF(temp);
2156+
if (x_is_odd == -1) {
2157+
Py_DECREF(x);
2158+
goto Done;
2159+
}
2160+
whole_us = 2.0 * round((leftover_us + x_is_odd) * 0.5) - x_is_odd;
2161+
}
2162+
2163+
temp = PyLong_FromLong(whole_us);
2164+
21522165
if (temp == NULL) {
21532166
Py_DECREF(x);
21542167
goto Done;
@@ -5351,12 +5364,12 @@ PyInit__datetime(void)
53515364
assert(DI100Y == 25 * DI4Y - 1);
53525365
assert(DI100Y == days_before_year(100+1));
53535366

5354-
us_per_us = PyLong_FromLong(1);
5367+
one = PyLong_FromLong(1);
53555368
us_per_ms = PyLong_FromLong(1000);
53565369
us_per_second = PyLong_FromLong(1000000);
53575370
us_per_minute = PyLong_FromLong(60000000);
53585371
seconds_per_day = PyLong_FromLong(24 * 3600);
5359-
if (us_per_us == NULL || us_per_ms == NULL || us_per_second == NULL ||
5372+
if (one == NULL || us_per_ms == NULL || us_per_second == NULL ||
53605373
us_per_minute == NULL || seconds_per_day == NULL)
53615374
return NULL;
53625375

0 commit comments

Comments
 (0)