From 13f93325d3fe27722a24167aa14647848d8681f4 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Mon, 22 Mar 2021 10:48:14 -0500 Subject: [PATCH] total_seconds: Fix precision problem Since CircuitPython floats have very small precision, the "total_seconds" method was not very useful for large timedeltas. Instead of always returning a float, use pure-integer arithmetic (and return an int) if either: * the length of time is big (1<<21 is 2097152 seconds or about 24 days) * the number of microseconds is zero Otherwise, for small values with nonzero microseconds, use floating-point math. The cut-off point was chosen because in CircuitPython float arithmetic, 2097151.0+0.5 is different from 2097151.0, but 2097152.0+0.5 and 2097152.0 are *the same float*. --- adafruit_datetime.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/adafruit_datetime.py b/adafruit_datetime.py index cb4fef9..f3c442f 100755 --- a/adafruit_datetime.py +++ b/adafruit_datetime.py @@ -429,9 +429,15 @@ def microseconds(self): # Instance methods def total_seconds(self): """Return the total number of seconds contained in the duration.""" - return ( - (self._days * 86400 + self._seconds) * 10 ** 6 + self._microseconds - ) / 10 ** 6 + # If the duration is less than a threshold duration, and microseconds + # is nonzero, then the result is a float. Otherwise, the result is a + # (possibly long) integer. This differs from standard Python where the + # result is always a float, because the precision of CircuitPython + # floats is considerably smaller than on standard Python. + seconds = self._days * 86400 + self._seconds + if self._microseconds != 0 and abs(seconds) < (1 << 21): + seconds += self._microseconds / 10 ** 6 + return seconds def __repr__(self): args = []