Issue #8644: Improve accuracy of timedelta.total_seconds, by doing intermediate
computations with integer arithmetic instead of floating point. td.total_seconds() now agrees with td / timedelta(seconds = 1). Thanks Alexander Belopolsky for the patch.
This commit is contained in:
parent
161b024b6d
commit
0381e3f16a
@ -287,7 +287,10 @@ Instance methods:
|
|||||||
.. method:: timedelta.total_seconds()
|
.. method:: timedelta.total_seconds()
|
||||||
|
|
||||||
Return the total number of seconds contained in the duration. Equivalent to
|
Return the total number of seconds contained in the duration. Equivalent to
|
||||||
``td.microseconds / 1000000 + td.seconds + td.days * 24 * 3600``.
|
``td / timedelta(seconds=1)``.
|
||||||
|
|
||||||
|
Note that for very large time intervals (greater than 270 years on
|
||||||
|
most platforms) this method will lose microsecond accuracy.
|
||||||
|
|
||||||
.. versionadded:: 3.2
|
.. versionadded:: 3.2
|
||||||
|
|
||||||
|
@ -264,6 +264,11 @@ class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
|
|||||||
for total_seconds in [123456.789012, -123456.789012, 0.123456, 0, 1e6]:
|
for total_seconds in [123456.789012, -123456.789012, 0.123456, 0, 1e6]:
|
||||||
td = timedelta(seconds=total_seconds)
|
td = timedelta(seconds=total_seconds)
|
||||||
self.assertEqual(td.total_seconds(), total_seconds)
|
self.assertEqual(td.total_seconds(), total_seconds)
|
||||||
|
# Issue8644: Test that td.total_seconds() has the same
|
||||||
|
# accuracy as td / timedelta(seconds=1).
|
||||||
|
for ms in [-1, -2, -123]:
|
||||||
|
td = timedelta(microseconds=ms)
|
||||||
|
self.assertEqual(td.total_seconds(), td / timedelta(seconds=1))
|
||||||
|
|
||||||
def test_carries(self):
|
def test_carries(self):
|
||||||
t1 = timedelta(days=100,
|
t1 = timedelta(days=100,
|
||||||
|
@ -1105,6 +1105,11 @@ Library
|
|||||||
Extension Modules
|
Extension Modules
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- Issue #8644: The accuracy of td.total_seconds() has been improved (by
|
||||||
|
calculating with integer arithmetic instead of float arithmetic internally):
|
||||||
|
the result is now always correctly rounded, and is equivalent to td /
|
||||||
|
timedelta(seconds=1).
|
||||||
|
|
||||||
- Issue #2706: Allow division of a timedelta by another timedelta:
|
- Issue #2706: Allow division of a timedelta by another timedelta:
|
||||||
timedelta / timedelta, timedelta % timedelta, timedelta // timedelta
|
timedelta / timedelta, timedelta % timedelta, timedelta // timedelta
|
||||||
and divmod(timedelta, timedelta) are all supported.
|
and divmod(timedelta, timedelta) are all supported.
|
||||||
|
@ -2211,9 +2211,25 @@ delta_getstate(PyDateTime_Delta *self)
|
|||||||
static PyObject *
|
static PyObject *
|
||||||
delta_total_seconds(PyObject *self)
|
delta_total_seconds(PyObject *self)
|
||||||
{
|
{
|
||||||
return PyFloat_FromDouble(GET_TD_MICROSECONDS(self) / 1000000.0 +
|
PyObject *total_seconds;
|
||||||
GET_TD_SECONDS(self) +
|
PyObject *total_microseconds;
|
||||||
GET_TD_DAYS(self) * 24.0 * 3600.0);
|
PyObject *one_million;
|
||||||
|
|
||||||
|
total_microseconds = delta_to_microseconds((PyDateTime_Delta *)self);
|
||||||
|
if (total_microseconds == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
one_million = PyLong_FromLong(1000000L);
|
||||||
|
if (one_million == NULL) {
|
||||||
|
Py_DECREF(total_microseconds);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
total_seconds = PyNumber_TrueDivide(total_microseconds, one_million);
|
||||||
|
|
||||||
|
Py_DECREF(total_microseconds);
|
||||||
|
Py_DECREF(one_million);
|
||||||
|
return total_seconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
Loading…
x
Reference in New Issue
Block a user