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:
Mark Dickinson 2010-05-08 14:35:02 +00:00
parent 161b024b6d
commit 0381e3f16a
4 changed files with 33 additions and 4 deletions

View File

@ -287,7 +287,10 @@ Instance methods:
.. method:: timedelta.total_seconds()
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

View File

@ -264,6 +264,11 @@ class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
for total_seconds in [123456.789012, -123456.789012, 0.123456, 0, 1e6]:
td = timedelta(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):
t1 = timedelta(days=100,

View File

@ -1105,6 +1105,11 @@ Library
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:
timedelta / timedelta, timedelta % timedelta, timedelta // timedelta
and divmod(timedelta, timedelta) are all supported.

View File

@ -2211,9 +2211,25 @@ delta_getstate(PyDateTime_Delta *self)
static PyObject *
delta_total_seconds(PyObject *self)
{
return PyFloat_FromDouble(GET_TD_MICROSECONDS(self) / 1000000.0 +
GET_TD_SECONDS(self) +
GET_TD_DAYS(self) * 24.0 * 3600.0);
PyObject *total_seconds;
PyObject *total_microseconds;
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 *