[Python-checkins] CVS: python/nondist/sandbox/datetime datetime.py,1.45,1.46

Tim Peters tim_one@users.sourceforge.net
Mon, 04 Mar 2002 14:52:09 -0800


Update of /cvsroot/python/python/nondist/sandbox/datetime
In directory usw-pr-cvs1:/tmp/cvs-serv23648

Modified Files:
	datetime.py 
Log Message:
timedelta.__init__():  Massive rewrite to deal correctly with both the
huge dynamic range of floats and the unbounded precision of longs, while
also isolating the subcomputations that can be reliably done with 32-bit
ints (and asserting that those won't overflow when redone in C -- but
assuming Python ints are 32 bits, for sanity's sake).


Index: datetime.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/datetime.py,v
retrieving revision 1.45
retrieving revision 1.46
diff -C2 -d -r1.45 -r1.46
*** datetime.py	4 Mar 2002 20:24:12 -0000	1.45
--- datetime.py	4 Mar 2002 22:52:07 -0000	1.46
***************
*** 305,336 ****
                   # XXX The following should only be used as keyword args:
                   milliseconds=0, minutes=0, hours=0, weeks=0):
          # Normalize everything to days, seconds, microseconds.
!         # Convert everything to float first, else overflow-checking in C
!         # is going to be a nightmare.
!         days += weeks*7.
!         seconds += minutes*60. + hours*3600.
!         microseconds += milliseconds*1000.
!         # Get rid of all fractions.
!         dayfrac, days = _math.modf(days)
!         daysecondsfrac, daysecondswhole = _math.modf(dayfrac * (24.*3600.))
!         secondsfrac, seconds = _math.modf(seconds)
!         seconds += daysecondswhole
          secondsfrac += daysecondsfrac
!         microseconds += secondsfrac*1e6
!         microseconds = round(microseconds)
!         assert _math.modf(days)[0] == 0.0
!         assert _math.modf(seconds)[0] == 0.0
!         assert _math.modf(microseconds)[0] == 0.0
!         # Propagate carry from us to s, from s to d
!         s, us = divmod(microseconds, 1000000)
!         assert us == int(us) and 0 <= us < 1000000
!         d, s = divmod(s + seconds, 24*3600)
!         assert s == int(s) and 0 <= s < 24*3600
          d += days
!         assert d == long(d)
!         # d may be < 0
!         self.__days = int(d)
!         self.__seconds = int(s)
!         self.__microseconds = int(us)
  
      def __repr__(self):
--- 305,406 ----
                   # XXX The following should only be used as keyword args:
                   milliseconds=0, minutes=0, hours=0, weeks=0):
+         # Doing this efficiently and accurately in C is going to be difficult
+         # and error-prone, due to ubiquitous overflow possibilities, and that
+         # C double doesn't have enough bits of precision to represent
+         # microseconds over 10K years faithfully.  The code here tries to make
+         # explicit where go-fast assumptions can be relied on, in order to
+         # guide the C implementation; it's way more convoluted than speed-
+         # ignoring auto-overflow-to-long idiomatic Python could be.
+ 
+         # XXX Check that all inputs are ints, longs or floats.
+ 
+         # Final values, all integer.
+         # s and us fit in 32-bit signed ints; d isn't bounded.
+         d = s = us = 0
+ 
          # Normalize everything to days, seconds, microseconds.
!         days += weeks*7
!         seconds += minutes*60 + hours*3600
!         microseconds += milliseconds*1000
! 
!         # Get rid of all fractions, and normalize s and us.
!         # Take a deep breath <wink>.
!         if isinstance(days, float):
!             dayfrac, days = _math.modf(days)
!             daysecondsfrac, daysecondswhole = _math.modf(dayfrac * (24.*3600.))
!             assert daysecondswhole == int(daysecondswhole)  # can't overflow
!             daysecondswhole = int(daysecondswhole)
!             assert days == long(days)
!             d = long(days)
!         else:
!             daysecondsfrac, daysecondswhole = 0.0, 0
!             d = days
!         assert isinstance(daysecondsfrac, float)
!         assert isinstance(daysecondswhole, int)
!         assert 0 <= daysecondswhole < 24*3600
!         assert isinstance(d, (int, long))
!         # days isn't referenced again before redefinition
! 
!         if isinstance(seconds, float):
!             secondsfrac, seconds = _math.modf(seconds)
!             assert seconds == long(seconds)
!             seconds = long(seconds)
!         else:
!             secondsfrac = 0.0
!         assert isinstance(secondsfrac, float)
!         assert isinstance(seconds, (int, long))
!         days, seconds = divmod(seconds, 24*3600)
!         d += days
!         s = int(seconds)    # can't overflow
!         s += daysecondswhole    # can't overflow
!         assert isinstance(s, int)
!         assert 0 <= s < 2*24*3600
!         # seconds isn't referenced again before redefinition
! 
          secondsfrac += daysecondsfrac
!         assert -2.0 <= secondsfrac <= 2.0
!         usdouble = secondsfrac * 1e6
!         assert -2.1e6 < usdouble < 2.1e6    # exact value not critical
!         # secondsfrac & daysecondsfrac aren't referenced again
! 
!         if isinstance(microseconds, float):
!             microseconds += usdouble
!             microseconds = round(microseconds)
!             seconds, microseconds = divmod(microseconds, 1e6)
!             assert microseconds == int(microseconds)
!             assert seconds == long(seconds)
!             days, seconds = divmod(seconds, 24.*3600.)
!             assert days == long(days)
!             assert seconds == int(seconds)
!             d += long(days)
!             s += int(seconds)   # can't overflow
!             assert isinstance(s, int)
!         else:
!             seconds, microseconds = divmod(microseconds, 1000000)
!             days, seconds = divmod(seconds, 24*3600)
!             d += days
!             s += int(seconds)    # can't overflow
!             assert isinstance(s, int)
!             microseconds = float(microseconds)
!             microseconds += usdouble
!             microseconds = round(microseconds)
! 
!         # Just a little bit of carrying possible for microseconds and seconds.
!         assert isinstance(microseconds, float)
!         assert 0 <= microseconds <= 3.1e6
!         assert int(microseconds) == microseconds
!         us = int(microseconds)
!         seconds, us = divmod(us, 1000000)
!         s += seconds    # cant't overflow
!         assert isinstance(s, int)
!         days, s = divmod(s, 24*3600)
          d += days
! 
!         assert isinstance(d, (int, long))
!         assert isinstance(s, int) and 0 <= s < 24*3600
!         assert isinstance(us, int) and 0 <= us < 1000000
!         self.__days = d
!         self.__seconds = s
!         self.__microseconds = us
  
      def __repr__(self):