Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions Lib/test/test_math.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -235,6 +235,10 @@ def __init__(self, value):
def __index__(self):
return self.value

class BadDescr:
def __get__(self, obj, objtype=None):
raise ValueError

class MathTests(unittest.TestCase):

def ftest(self, name, got, expected, ulp_tol=5, abs_tol=0.0):
Expand DownExpand Up@@ -324,6 +328,7 @@ def testAtan2(self):
self.ftest('atan2(0, 1)', math.atan2(0, 1), 0)
self.ftest('atan2(1, 1)', math.atan2(1, 1), math.pi/4)
self.ftest('atan2(1, 0)', math.atan2(1, 0), math.pi/2)
self.ftest('atan2(1, -1)', math.atan2(1, -1), 3*math.pi/4)

# math.atan2(0, x)
self.ftest('atan2(0., -inf)', math.atan2(0., NINF), math.pi)
Expand DownExpand Up@@ -417,16 +422,22 @@ def __ceil__(self):
return 42
class TestNoCeil:
pass
class TestBadCeil:
__ceil__ = BadDescr()
self.assertEqual(math.ceil(TestCeil()), 42)
self.assertEqual(math.ceil(FloatCeil()), 42)
self.assertEqual(math.ceil(FloatLike(42.5)), 43)
self.assertRaises(TypeError, math.ceil, TestNoCeil())
self.assertRaises(ValueError, math.ceil, TestBadCeil())

t = TestNoCeil()
t.__ceil__ = lambda *args: args
self.assertRaises(TypeError, math.ceil, t)
self.assertRaises(TypeError, math.ceil, t, 0)

self.assertEqual(math.ceil(FloatLike(+1.0)), +1.0)
self.assertEqual(math.ceil(FloatLike(-1.0)), -1.0)

@requires_IEEE_754
def testCopysign(self):
self.assertEqual(math.copysign(1, 42), 1.0)
Expand DownExpand Up@@ -567,16 +578,22 @@ def __floor__(self):
return 42
class TestNoFloor:
pass
class TestBadFloor:
__floor__ = BadDescr()
self.assertEqual(math.floor(TestFloor()), 42)
self.assertEqual(math.floor(FloatFloor()), 42)
self.assertEqual(math.floor(FloatLike(41.9)), 41)
self.assertRaises(TypeError, math.floor, TestNoFloor())
self.assertRaises(ValueError, math.floor, TestBadFloor())

t = TestNoFloor()
t.__floor__ = lambda *args: args
self.assertRaises(TypeError, math.floor, t)
self.assertRaises(TypeError, math.floor, t, 0)

self.assertEqual(math.floor(FloatLike(+1.0)), +1.0)
self.assertEqual(math.floor(FloatLike(-1.0)), -1.0)

def testFmod(self):
self.assertRaises(TypeError, math.fmod)
self.ftest('fmod(10, 1)', math.fmod(10, 1), 0.0)
Expand All@@ -598,6 +615,7 @@ def testFmod(self):
self.assertEqual(math.fmod(-3.0, NINF), -3.0)
self.assertEqual(math.fmod(0.0, 3.0), 0.0)
self.assertEqual(math.fmod(0.0, NINF), 0.0)
self.assertRaises(ValueError, math.fmod, INF, INF)

def testFrexp(self):
self.assertRaises(TypeError, math.frexp)
Expand DownExpand Up@@ -714,6 +732,11 @@ def msum(iterable):
s = msum(vals)
self.assertEqual(msum(vals), math.fsum(vals))

self.assertEqual(math.fsum([1.0, math.inf]), math.inf)
self.assertRaises(OverflowError, math.fsum, [1e+308, 1e+308])
self.assertRaises(ValueError, math.fsum, [math.inf, -math.inf])
self.assertRaises(TypeError, math.fsum, ['spam'])

def testGcd(self):
gcd = math.gcd
self.assertEqual(gcd(0, 0), 0)
Expand DownExpand Up@@ -831,6 +854,8 @@ def testHypot(self):
scale = FLOAT_MIN / 2.0 ** exp
self.assertEqual(math.hypot(4*scale, 3*scale), 5*scale)

self.assertRaises(TypeError, math.hypot, *([1.0]*18), 'spam')

@requires_IEEE_754
@unittest.skipIf(HAVE_DOUBLE_ROUNDING,
"hypot() loses accuracy on machines with double rounding")
Expand DownExpand Up@@ -966,13 +991,19 @@ class T(tuple):
dist((1, 2, 3, 4), (5, 6, 7))
with self.assertRaises(ValueError): # Check dimension agree
dist((1, 2, 3), (4, 5, 6, 7))
with self.assertRaises(TypeError):
dist((1,)*17 + ("spam",), (1,)*18)
with self.assertRaises(TypeError): # Rejects invalid types
dist("abc", "xyz")
int_too_big_for_float = 10 ** (sys.float_info.max_10_exp + 5)
with self.assertRaises((ValueError, OverflowError)):
dist((1, int_too_big_for_float), (2, 3))
with self.assertRaises((ValueError, OverflowError)):
dist((2, 3), (1, int_too_big_for_float))
with self.assertRaises(TypeError):
dist((1,), 2)
with self.assertRaises(TypeError):
dist([1], 2)

# Verify that the one dimensional case is equivalent to abs()
for i in range(20):
Expand DownExpand Up@@ -1111,6 +1142,7 @@ def test_lcm(self):

def testLdexp(self):
self.assertRaises(TypeError, math.ldexp)
self.assertRaises(TypeError, math.ldexp, 2.0, 1.1)
self.ftest('ldexp(0,1)', math.ldexp(0,1), 0)
self.ftest('ldexp(1,1)', math.ldexp(1,1), 2)
self.ftest('ldexp(1,-1)', math.ldexp(1,-1), 0.5)
Expand DownExpand Up@@ -1153,6 +1185,7 @@ def testLog(self):
2302.5850929940457)
self.assertRaises(ValueError, math.log, -1.5)
self.assertRaises(ValueError, math.log, -10**1000)
self.assertRaises(ValueError, math.log, 10, -10)
self.assertRaises(ValueError, math.log, NINF)
self.assertEqual(math.log(INF), INF)
self.assertTrue(math.isnan(math.log(NAN)))
Expand DownExpand Up@@ -2378,6 +2411,14 @@ def __float__(self):
# argument to a float.
self.assertFalse(getattr(y, "converted", False))

def test_input_exceptions(self):
self.assertRaises(TypeError, math.exp, "spam")
self.assertRaises(TypeError, math.erf, "spam")
self.assertRaises(TypeError, math.atan2, "spam", 1.0)
self.assertRaises(TypeError, math.atan2, 1.0, "spam")
self.assertRaises(TypeError, math.atan2, 1.0)
self.assertRaises(TypeError, math.atan2, 1.0, 2.0, 3.0)

# Custom assertions.

def assertIsNaN(self, value):
Expand Down
17 changes: 7 additions & 10 deletions Modules/mathmodule.c
Original file line numberDiff line numberDiff line change
Expand Up@@ -2195,12 +2195,10 @@ math_modf_impl(PyObject *module, double x)
double y;
/* some platforms don't do the right thing for NaNs and
infinities, so we take care of special cases directly. */
if (!Py_IS_FINITE(x)) {
if (Py_IS_INFINITY(x))
return Py_BuildValue("(dd)", copysign(0., x), x);
else if (Py_IS_NAN(x))
return Py_BuildValue("(dd)", x, x);
}
if (Py_IS_INFINITY(x))
return Py_BuildValue("(dd)", copysign(0., x), x);
else if (Py_IS_NAN(x))
return Py_BuildValue("(dd)", x, x);
Comment thread
skirpichev marked this conversation as resolved.

errno = 0;
x = modf(x, &y);
Expand DownExpand Up@@ -2950,7 +2948,8 @@ math_pow_impl(PyObject *module, double x, double y)
else /* y < 0. */
r = odd_y ? copysign(0., x) : 0.;
}
else if (Py_IS_INFINITY(y)) {
else {
assert(Py_IS_INFINITY(y));
if (fabs(x) == 1.0)
r = 1.;
else if (y > 0. && fabs(x) > 1.0)
Expand DownExpand Up@@ -3480,9 +3479,7 @@ static const uint8_t factorial_trailing_zeros[] = {
static PyObject *
perm_comb_small(unsigned long long n, unsigned long long k, int iscomb)
{
if (k == 0) {
Comment thread
skirpichev marked this conversation as resolved.
return PyLong_FromLong(1);
}
assert(k != 0);

/* For small enough n and k the result fits in the 64-bit range and can
* be calculated without allocating intermediate PyLong objects. */
Expand Down