summaryrefslogtreecommitdiff
path: root/Python
diff options
context:
space:
mode:
authorVictor Stinner <victor.stinner@gmail.com>2015-09-09 01:02:23 +0200
committerVictor Stinner <victor.stinner@gmail.com>2015-09-09 01:02:23 +0200
commitfb8809cbfa9e3302900ffb10b732626ce864157c (patch)
treeeb958a00162e2645140cc416f534d695867ef277 /Python
parentbbd2c26bb917642217efd84fc4a3dd0b3e0f0a20 (diff)
downloadcpython-fb8809cbfa9e3302900ffb10b732626ce864157c.tar.gz
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.
Diffstat (limited to 'Python')
-rw-r--r--Python/pytime.c71
1 files changed, 29 insertions, 42 deletions
diff --git a/Python/pytime.c b/Python/pytime.c
index 036447365c..9f7ee2d0ed 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
@@ -370,10 +369,14 @@ _PyTime_Divide(_PyTime_t t, _PyTime_t k, _PyTime_round_t round)
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;
}