mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 05:31:20 +00:00 
			
		
		
		
	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
					
				
					 4 changed files with 33 additions and 4 deletions
				
			
		|  | @ -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 @@ def test_total_seconds(self): | ||||||
|         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…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Mark Dickinson
						Mark Dickinson