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() | ||||
| 
 | ||||
|    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 | ||||
| 
 | ||||
|  |  | |||
|  | @ -264,6 +264,11 @@ def test_total_seconds(self): | |||
|         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, | ||||
|  |  | |||
|  | @ -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. | ||||
|  |  | |||
|  | @ -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 * | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Mark Dickinson
						Mark Dickinson