Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
gh-102837: few coverage nitpicks for the math module
- input checks for math_1(L989), math_1a(L1023), math_2(L1064,L1071),
  hypot(L2682), log(L2307), ldexp(L2168) and dist(L2587,L2588,L2628).
- rewrite math_floor like math_ceil (cover L1239)
- drop inaccessible "if" branch (L3518) in perm_comb_small()
- improve fsum coverage for exceptional cases (L1433,L1438,L1451,L1497),
  ditto fmod(L2378)
- rewrite modf to fix inaccessible case(L2229), ditto for pow(L2988)

(all line numbers wrt the main branch at 5e6661b)
  • Loading branch information
skirpichev committed Mar 20, 2023
commit 68b2e428811d31783f70c9e051064de24f7bcbac
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 @@ -2364,6 +2381,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
31 changes: 10 additions & 21 deletions Modules/mathmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1219,13 +1219,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 @@ -1235,10 +1229,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);
Comment thread
skirpichev marked this conversation as resolved.
Outdated
if (x == -1.0 && PyErr_Occurred())
return NULL;

return PyLong_FromDouble(floor(x));
}

Expand Down Expand Up @@ -2223,12 +2218,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 Down Expand Up @@ -2985,7 +2978,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) */
Comment thread
skirpichev marked this conversation as resolved.
Outdated
if (fabs(x) == 1.0)
r = 1.;
else if (y > 0. && fabs(x) > 1.0)
Expand Down Expand Up @@ -3515,10 +3508,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) {
Comment thread
skirpichev marked this conversation as 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