mirror of https://github.com/python/cpython.git
Fixes #23521: Corrected pure python implementation of timedelta division.
* Eliminated OverflowError from timedelta * float for some floats; * Corrected rounding in timedlta true division.
This commit is contained in:
parent
cf265fd02a
commit
24d3deefcf
|
@ -280,6 +280,25 @@ def _cmperror(x, y):
|
||||||
raise TypeError("can't compare '%s' to '%s'" % (
|
raise TypeError("can't compare '%s' to '%s'" % (
|
||||||
type(x).__name__, type(y).__name__))
|
type(x).__name__, type(y).__name__))
|
||||||
|
|
||||||
|
def _divide_and_round(a, b):
|
||||||
|
"""divide a by b and round result to the nearest integer
|
||||||
|
|
||||||
|
When the ratio is exactly half-way between two integers,
|
||||||
|
the even integer is returned.
|
||||||
|
"""
|
||||||
|
# Based on the reference implementation for divmod_near
|
||||||
|
# in Objects/longobject.c.
|
||||||
|
q, r = divmod(a, b)
|
||||||
|
# round up if either r / b > 0.5, or r / b == 0.5 and q is odd.
|
||||||
|
# The expression r / b > 0.5 is equivalent to 2 * r > b if b is
|
||||||
|
# positive, 2 * r < b if b negative.
|
||||||
|
r *= 2
|
||||||
|
greater_than_half = r > b if b > 0 else r < b
|
||||||
|
if greater_than_half or r == b and q % 2 == 1:
|
||||||
|
q += 1
|
||||||
|
|
||||||
|
return q
|
||||||
|
|
||||||
class timedelta:
|
class timedelta:
|
||||||
"""Represent the difference between two datetime objects.
|
"""Represent the difference between two datetime objects.
|
||||||
|
|
||||||
|
@ -506,8 +525,9 @@ def __mul__(self, other):
|
||||||
self._seconds * other,
|
self._seconds * other,
|
||||||
self._microseconds * other)
|
self._microseconds * other)
|
||||||
if isinstance(other, float):
|
if isinstance(other, float):
|
||||||
|
usec = self._to_microseconds()
|
||||||
a, b = other.as_integer_ratio()
|
a, b = other.as_integer_ratio()
|
||||||
return self * a / b
|
return timedelta(0, 0, _divide_and_round(usec * a, b))
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
|
|
||||||
__rmul__ = __mul__
|
__rmul__ = __mul__
|
||||||
|
@ -532,10 +552,10 @@ def __truediv__(self, other):
|
||||||
if isinstance(other, timedelta):
|
if isinstance(other, timedelta):
|
||||||
return usec / other._to_microseconds()
|
return usec / other._to_microseconds()
|
||||||
if isinstance(other, int):
|
if isinstance(other, int):
|
||||||
return timedelta(0, 0, usec / other)
|
return timedelta(0, 0, _divide_and_round(usec, other))
|
||||||
if isinstance(other, float):
|
if isinstance(other, float):
|
||||||
a, b = other.as_integer_ratio()
|
a, b = other.as_integer_ratio()
|
||||||
return timedelta(0, 0, b * usec / a)
|
return timedelta(0, 0, _divide_and_round(b * usec, a))
|
||||||
|
|
||||||
def __mod__(self, other):
|
def __mod__(self, other):
|
||||||
if isinstance(other, timedelta):
|
if isinstance(other, timedelta):
|
||||||
|
|
|
@ -50,6 +50,33 @@ def test_constants(self):
|
||||||
self.assertEqual(datetime.MINYEAR, 1)
|
self.assertEqual(datetime.MINYEAR, 1)
|
||||||
self.assertEqual(datetime.MAXYEAR, 9999)
|
self.assertEqual(datetime.MAXYEAR, 9999)
|
||||||
|
|
||||||
|
def test_divide_and_round(self):
|
||||||
|
if '_Fast' in str(self):
|
||||||
|
return
|
||||||
|
dar = datetime_module._divide_and_round
|
||||||
|
|
||||||
|
self.assertEqual(dar(-10, -3), 3)
|
||||||
|
self.assertEqual(dar(5, -2), -2)
|
||||||
|
|
||||||
|
# four cases: (2 signs of a) x (2 signs of b)
|
||||||
|
self.assertEqual(dar(7, 3), 2)
|
||||||
|
self.assertEqual(dar(-7, 3), -2)
|
||||||
|
self.assertEqual(dar(7, -3), -2)
|
||||||
|
self.assertEqual(dar(-7, -3), 2)
|
||||||
|
|
||||||
|
# ties to even - eight cases:
|
||||||
|
# (2 signs of a) x (2 signs of b) x (even / odd quotient)
|
||||||
|
self.assertEqual(dar(10, 4), 2)
|
||||||
|
self.assertEqual(dar(-10, 4), -2)
|
||||||
|
self.assertEqual(dar(10, -4), -2)
|
||||||
|
self.assertEqual(dar(-10, -4), 2)
|
||||||
|
|
||||||
|
self.assertEqual(dar(6, 4), 2)
|
||||||
|
self.assertEqual(dar(-6, 4), -2)
|
||||||
|
self.assertEqual(dar(6, -4), -2)
|
||||||
|
self.assertEqual(dar(-6, -4), 2)
|
||||||
|
|
||||||
|
|
||||||
#############################################################################
|
#############################################################################
|
||||||
# tzinfo tests
|
# tzinfo tests
|
||||||
|
|
||||||
|
@ -382,6 +409,10 @@ def test_computations(self):
|
||||||
eq((-3*us) * 0.5, -2*us)
|
eq((-3*us) * 0.5, -2*us)
|
||||||
eq((-5*us) * 0.5, -2*us)
|
eq((-5*us) * 0.5, -2*us)
|
||||||
|
|
||||||
|
# Issue #23521
|
||||||
|
eq(td(seconds=1) * 0.123456, td(microseconds=123456))
|
||||||
|
eq(td(seconds=1) * 0.6112295, td(microseconds=611229))
|
||||||
|
|
||||||
# Division by int and float
|
# Division by int and float
|
||||||
eq((3*us) / 2, 2*us)
|
eq((3*us) / 2, 2*us)
|
||||||
eq((5*us) / 2, 2*us)
|
eq((5*us) / 2, 2*us)
|
||||||
|
@ -396,6 +427,9 @@ def test_computations(self):
|
||||||
for i in range(-10, 10):
|
for i in range(-10, 10):
|
||||||
eq((i*us/-3)//us, round(i/-3))
|
eq((i*us/-3)//us, round(i/-3))
|
||||||
|
|
||||||
|
# Issue #23521
|
||||||
|
eq(td(seconds=1) / (1 / 0.6112295), td(microseconds=611229))
|
||||||
|
|
||||||
# Issue #11576
|
# Issue #11576
|
||||||
eq(td(999999999, 86399, 999999) - td(999999999, 86399, 999998),
|
eq(td(999999999, 86399, 999999) - td(999999999, 86399, 999998),
|
||||||
td(0, 0, 1))
|
td(0, 0, 1))
|
||||||
|
|
|
@ -12,6 +12,10 @@ Core and Builtins
|
||||||
|
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
- Issue #23521: Corrected pure python implementation of timedelta division.
|
||||||
|
|
||||||
|
* Eliminated OverflowError from timedelta * float for some floats;
|
||||||
|
* Corrected rounding in timedlta true division.
|
||||||
|
|
||||||
- Issue #21619: Popen objects no longer leave a zombie after exit in the with
|
- Issue #21619: Popen objects no longer leave a zombie after exit in the with
|
||||||
statement if the pipe was broken. Patch by Martin Panter.
|
statement if the pipe was broken. Patch by Martin Panter.
|
||||||
|
|
Loading…
Reference in New Issue