@@ -663,14 +663,6 @@ class timedelta:
663663
664664 def __new__ (cls , days = 0 , seconds = 0 , microseconds = 0 ,
665665 milliseconds = 0 , minutes = 0 , hours = 0 , weeks = 0 ):
666- # Doing this efficiently and accurately in C is going to be difficult
667- # and error-prone, due to ubiquitous overflow possibilities, and that
668- # C double doesn't have enough bits of precision to represent
669- # microseconds over 10K years faithfully. The code here tries to make
670- # explicit where go-fast assumptions can be relied on, in order to
671- # guide the C implementation; it's way more convoluted than speed-
672- # ignoring auto-overflow-to-long idiomatic Python could be.
673-
674666 for name , value in (
675667 ("days" , days ),
676668 ("seconds" , seconds ),
@@ -685,80 +677,35 @@ def __new__(cls, days=0, seconds=0, microseconds=0,
685677 f"unsupported type for timedelta { name } component: { type (value ).__name__ } "
686678 )
687679
688- # Final values, all integer.
689- # s and us fit in 32-bit signed ints; d isn't bounded.
690- d = s = us = 0
691-
692- # Normalize everything to days, seconds, microseconds.
693- days += weeks * 7
694- seconds += minutes * 60 + hours * 3600
695- microseconds += milliseconds * 1000
696-
697- # Get rid of all fractions, and normalize s and us.
698- # Take a deep breath <wink>.
699- if isinstance (days , float ):
700- dayfrac , days = _math .modf (days )
701- daysecondsfrac , daysecondswhole = _math .modf (dayfrac * (24. * 3600. ))
702- assert daysecondswhole == int (daysecondswhole ) # can't overflow
703- s = int (daysecondswhole )
704- assert days == int (days )
705- d = int (days )
706- else :
707- daysecondsfrac = 0.0
708- d = days
709- assert isinstance (daysecondsfrac , float )
710- assert abs (daysecondsfrac ) <= 1.0
711- assert isinstance (d , int )
712- assert abs (s ) <= 24 * 3600
713- # days isn't referenced again before redefinition
714-
715- if isinstance (seconds , float ):
716- secondsfrac , seconds = _math .modf (seconds )
717- assert seconds == int (seconds )
718- seconds = int (seconds )
719- secondsfrac += daysecondsfrac
720- assert abs (secondsfrac ) <= 2.0
721- else :
722- secondsfrac = daysecondsfrac
723- # daysecondsfrac isn't referenced again
724- assert isinstance (secondsfrac , float )
725- assert abs (secondsfrac ) <= 2.0
726-
727- assert isinstance (seconds , int )
728- days , seconds = divmod (seconds , 24 * 3600 )
729- d += days
730- s += int (seconds ) # can't overflow
731- assert isinstance (s , int )
732- assert abs (s ) <= 2 * 24 * 3600
733- # seconds isn't referenced again before redefinition
734-
735- usdouble = secondsfrac * 1e6
736- assert abs (usdouble ) < 2.1e6 # exact value not critical
737- # secondsfrac isn't referenced again
738-
739- if isinstance (microseconds , float ):
740- microseconds = round (microseconds + usdouble )
741- seconds , microseconds = divmod (microseconds , 1000000 )
742- days , seconds = divmod (seconds , 24 * 3600 )
743- d += days
744- s += seconds
745- else :
746- microseconds = int (microseconds )
747- seconds , microseconds = divmod (microseconds , 1000000 )
748- days , seconds = divmod (seconds , 24 * 3600 )
749- d += days
750- s += seconds
751- microseconds = round (microseconds + usdouble )
752- assert isinstance (s , int )
753- assert isinstance (microseconds , int )
754- assert abs (s ) <= 3 * 24 * 3600
755- assert abs (microseconds ) < 3.1e6
756-
757- # Just a little bit of carrying possible for microseconds and seconds.
758- seconds , us = divmod (microseconds , 1000000 )
759- s += seconds
760- days , s = divmod (s , 24 * 3600 )
761- d += days
680+ # Sum the exact number of microseconds, like the C implementation:
681+ # the integer part of each float is converted exactly, and only the
682+ # fractional microseconds left over are added up as a float and
683+ # rounded (half to even) at the end.
684+ us = 0
685+ leftover = 0.0
686+ for value , factor in (
687+ (microseconds , 1 ),
688+ (milliseconds , 1000 ),
689+ (seconds , 1000000 ),
690+ (minutes , 60 * 1000000 ),
691+ (hours , 3600 * 1000000 ),
692+ (days , 24 * 3600 * 1000000 ),
693+ (weeks , 7 * 24 * 3600 * 1000000 )
694+ ):
695+ if isinstance (value , float ):
696+ frac , whole = _math .modf (value )
697+ us += int (whole ) * factor
698+ frac , whole = _math .modf (frac * factor )
699+ us += int (whole )
700+ leftover += frac
701+ else :
702+ us += int (value ) * factor
703+ if leftover :
704+ n , m = leftover .as_integer_ratio ()
705+ us = _divide_and_round (us * m + n , m )
706+
707+ s , us = divmod (us , 1000000 )
708+ d , s = divmod (s , 24 * 3600 )
762709
763710 assert isinstance (d , int )
764711 assert isinstance (s , int ) and 0 <= s < 24 * 3600
0 commit comments