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