Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-102837: few coverage nitpicks for the math module #102523

Merged
merged 20 commits into from
Sep 3, 2023
Merged
Show file tree
Hide file tree
Changes from 9 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
25 changes: 25 additions & 0 deletions Lib/test/test_math.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,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 Down Expand Up @@ -598,6 +599,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 Down Expand Up @@ -714,6 +716,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 Down Expand Up @@ -831,6 +838,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 Down Expand Up @@ -966,13 +975,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 Down Expand Up @@ -1111,6 +1126,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 Down Expand Up @@ -1153,6 +1169,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 Down Expand Up @@ -2378,6 +2395,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
49 changes: 15 additions & 34 deletions Modules/mathmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -961,24 +961,16 @@ math_1(PyObject *arg, double (*func) (double), int can_overflow)
return NULL;
errno = 0;
r = (*func)(x);
if (Py_IS_NAN(r) && !Py_IS_NAN(x)) {
PyErr_SetString(PyExc_ValueError,
"math domain error"); /* invalid arg */
return NULL;
}
if (Py_IS_NAN(r) && !Py_IS_NAN(x))
errno = EDOM;
if (Py_IS_INFINITY(r) && Py_IS_FINITE(x)) {
if (can_overflow)
PyErr_SetString(PyExc_OverflowError,
"math range error"); /* overflow */
errno = ERANGE;
else
PyErr_SetString(PyExc_ValueError,
"math domain error"); /* singularity */
return NULL;
errno = EDOM;
}
if (Py_IS_FINITE(r) && errno && is_error(r))
/* this branch unnecessary on most platforms */
if (errno && is_error(r))
skirpichev marked this conversation as resolved.
Show resolved Hide resolved
return NULL;

return PyFloat_FromDouble(r);
}

Expand Down Expand Up @@ -1190,13 +1182,7 @@ static PyObject *
math_floor(PyObject *module, PyObject *number)
/*[clinic end generated code: output=c6a65c4884884b8a input=63af6b5d7ebcc3d6]*/
{
double x;

if (PyFloat_CheckExact(number)) {
x = PyFloat_AS_DOUBLE(number);
}
else
{
if (!PyFloat_CheckExact(number)) {
math_module_state *state = get_math_module_state(module);
PyObject *method = _PyObject_LookupSpecial(number, state->str___floor__);
if (method != NULL) {
Expand All @@ -1206,10 +1192,11 @@ math_floor(PyObject *module, PyObject *number)
}
if (PyErr_Occurred())
return NULL;
x = PyFloat_AsDouble(number);
if (x == -1.0 && PyErr_Occurred())
return NULL;
}
double x = PyFloat_AsDouble(number);
skirpichev marked this conversation as resolved.
Show resolved Hide resolved
if (x == -1.0 && PyErr_Occurred())
return NULL;

return PyLong_FromDouble(floor(x));
}

Expand Down Expand Up @@ -2194,12 +2181,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);
skirpichev marked this conversation as resolved.
Show resolved Hide resolved

errno = 0;
x = modf(x, &y);
Expand Down Expand Up @@ -2949,7 +2934,7 @@ 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 { /* Py_IS_INFINITY(y) */
skirpichev marked this conversation as resolved.
Show resolved Hide resolved
if (fabs(x) == 1.0)
r = 1.;
else if (y > 0. && fabs(x) > 1.0)
Expand Down Expand Up @@ -3479,10 +3464,6 @@ 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) {
skirpichev marked this conversation as resolved.
Show resolved Hide resolved
return PyLong_FromLong(1);
}

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