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