Skip to content

Commit

Permalink
pythongh-94906: Support multiple steps in math.nextafter
Browse files Browse the repository at this point in the history
  • Loading branch information
hauntsaninja committed Jul 16, 2022
1 parent 4b4439d commit 5ba2c27
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 16 deletions.
7 changes: 5 additions & 2 deletions Doc/library/math.rst
Original file line number Diff line number Diff line change
Expand Up @@ -229,9 +229,9 @@ Number-theoretic and representation functions
of *x* and are floats.


.. function:: nextafter(x, y)
.. function:: nextafter(x, y, /, *, steps=1)

Return the next floating-point value after *x* towards *y*.
Return the floating-point value *steps* steps after *x* towards *y*.

If *x* is equal to *y*, return *y*.

Expand All @@ -244,6 +244,9 @@ Number-theoretic and representation functions

See also :func:`math.ulp`.

.. versionchanged:: 3.12
Added the *steps* argument.

.. versionadded:: 3.9

.. function:: perm(n, k=None)
Expand Down
21 changes: 18 additions & 3 deletions Lib/test/test_math.py
Original file line number Diff line number Diff line change
Expand Up @@ -2036,11 +2036,20 @@ def test_nextafter(self):
float.fromhex('0x1.fffffffffffffp-1'))
self.assertEqual(math.nextafter(1.0, INF),
float.fromhex('0x1.0000000000001p+0'))
self.assertEqual(math.nextafter(1.0, -INF, steps=1),
float.fromhex('0x1.fffffffffffffp-1'))
self.assertEqual(math.nextafter(1.0, INF, steps=1),
float.fromhex('0x1.0000000000001p+0'))
self.assertEqual(math.nextafter(1.0, -INF, steps=3),
float.fromhex('0x1.ffffffffffffdp-1'))
self.assertEqual(math.nextafter(1.0, INF, steps=3),
float.fromhex('0x1.0000000000003p+0'))

# x == y: y is returned
self.assertEqual(math.nextafter(2.0, 2.0), 2.0)
self.assertEqualSign(math.nextafter(-0.0, +0.0), +0.0)
self.assertEqualSign(math.nextafter(+0.0, -0.0), -0.0)
for steps in range(1, 5):
self.assertEqual(math.nextafter(2.0, 2.0, steps=steps), 2.0)
self.assertEqualSign(math.nextafter(-0.0, +0.0, steps=steps), +0.0)
self.assertEqualSign(math.nextafter(+0.0, -0.0, steps=steps), -0.0)

# around 0.0
smallest_subnormal = sys.float_info.min * sys.float_info.epsilon
Expand All @@ -2065,6 +2074,12 @@ def test_nextafter(self):
self.assertIsNaN(math.nextafter(1.0, NAN))
self.assertIsNaN(math.nextafter(NAN, NAN))

with self.assertRaises(ValueError):
math.nextafter(1.0, INF, steps=0)
with self.assertRaises(ValueError):
math.nextafter(1.0, INF, steps=-1)


@requires_IEEE_754
def test_ulp(self):
self.assertEqual(math.ulp(1.0), sys.float_info.epsilon)
Expand Down
28 changes: 21 additions & 7 deletions Modules/clinic/mathmodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 14 additions & 4 deletions Modules/mathmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -3781,14 +3781,17 @@ math.nextafter
x: double
y: double
/
*
steps: int = 1
Return the next floating-point value after x towards y.
Return the floating-point value the given number of steps after x towards y.
[clinic start generated code]*/

static PyObject *
math_nextafter_impl(PyObject *module, double x, double y)
/*[clinic end generated code: output=750c8266c1c540ce input=02b2d50cd1d9f9b6]*/
math_nextafter_impl(PyObject *module, double x, double y, int steps)
/*[clinic end generated code: output=14190eb869199e5a input=e87d3b26a7611ff4]*/
{
int i;
#if defined(_AIX)
if (x == y) {
/* On AIX 7.1, libm nextafter(-0.0, +0.0) returns -0.0.
Expand All @@ -3802,7 +3805,14 @@ math_nextafter_impl(PyObject *module, double x, double y)
return PyFloat_FromDouble(y);
}
#endif
return PyFloat_FromDouble(nextafter(x, y));
if (steps < 1) {
PyErr_SetString(PyExc_ValueError, "steps must be >= 1");
return NULL;
}
for (i = 0; i < steps; i++) {
x = nextafter(x, y);
}
return PyFloat_FromDouble(x);
}


Expand Down

0 comments on commit 5ba2c27

Please sign in to comment.