Skip to content

Commit 5ba2c27

Browse files
committed
pythongh-94906: Support multiple steps in math.nextafter
1 parent 4b4439d commit 5ba2c27

File tree

4 files changed

+58
-16
lines changed

4 files changed

+58
-16
lines changed

Doc/library/math.rst

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,9 +229,9 @@ Number-theoretic and representation functions
229229
of *x* and are floats.
230230

231231

232-
.. function:: nextafter(x, y)
232+
.. function:: nextafter(x, y, /, *, steps=1)
233233

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

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

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

245245
See also :func:`math.ulp`.
246246

247+
.. versionchanged:: 3.12
248+
Added the *steps* argument.
249+
247250
.. versionadded:: 3.9
248251

249252
.. function:: perm(n, k=None)

Lib/test/test_math.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2036,11 +2036,20 @@ def test_nextafter(self):
20362036
float.fromhex('0x1.fffffffffffffp-1'))
20372037
self.assertEqual(math.nextafter(1.0, INF),
20382038
float.fromhex('0x1.0000000000001p+0'))
2039+
self.assertEqual(math.nextafter(1.0, -INF, steps=1),
2040+
float.fromhex('0x1.fffffffffffffp-1'))
2041+
self.assertEqual(math.nextafter(1.0, INF, steps=1),
2042+
float.fromhex('0x1.0000000000001p+0'))
2043+
self.assertEqual(math.nextafter(1.0, -INF, steps=3),
2044+
float.fromhex('0x1.ffffffffffffdp-1'))
2045+
self.assertEqual(math.nextafter(1.0, INF, steps=3),
2046+
float.fromhex('0x1.0000000000003p+0'))
20392047

20402048
# x == y: y is returned
2041-
self.assertEqual(math.nextafter(2.0, 2.0), 2.0)
2042-
self.assertEqualSign(math.nextafter(-0.0, +0.0), +0.0)
2043-
self.assertEqualSign(math.nextafter(+0.0, -0.0), -0.0)
2049+
for steps in range(1, 5):
2050+
self.assertEqual(math.nextafter(2.0, 2.0, steps=steps), 2.0)
2051+
self.assertEqualSign(math.nextafter(-0.0, +0.0, steps=steps), +0.0)
2052+
self.assertEqualSign(math.nextafter(+0.0, -0.0, steps=steps), -0.0)
20442053

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

2077+
with self.assertRaises(ValueError):
2078+
math.nextafter(1.0, INF, steps=0)
2079+
with self.assertRaises(ValueError):
2080+
math.nextafter(1.0, INF, steps=-1)
2081+
2082+
20682083
@requires_IEEE_754
20692084
def test_ulp(self):
20702085
self.assertEqual(math.ulp(1.0), sys.float_info.epsilon)

Modules/clinic/mathmodule.c.h

Lines changed: 21 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Modules/mathmodule.c

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3781,14 +3781,17 @@ math.nextafter
37813781
x: double
37823782
y: double
37833783
/
3784+
*
3785+
steps: int = 1
37843786
3785-
Return the next floating-point value after x towards y.
3787+
Return the floating-point value the given number of steps after x towards y.
37863788
[clinic start generated code]*/
37873789

37883790
static PyObject *
3789-
math_nextafter_impl(PyObject *module, double x, double y)
3790-
/*[clinic end generated code: output=750c8266c1c540ce input=02b2d50cd1d9f9b6]*/
3791+
math_nextafter_impl(PyObject *module, double x, double y, int steps)
3792+
/*[clinic end generated code: output=14190eb869199e5a input=e87d3b26a7611ff4]*/
37913793
{
3794+
int i;
37923795
#if defined(_AIX)
37933796
if (x == y) {
37943797
/* On AIX 7.1, libm nextafter(-0.0, +0.0) returns -0.0.
@@ -3802,7 +3805,14 @@ math_nextafter_impl(PyObject *module, double x, double y)
38023805
return PyFloat_FromDouble(y);
38033806
}
38043807
#endif
3805-
return PyFloat_FromDouble(nextafter(x, y));
3808+
if (steps < 1) {
3809+
PyErr_SetString(PyExc_ValueError, "steps must be >= 1");
3810+
return NULL;
3811+
}
3812+
for (i = 0; i < steps; i++) {
3813+
x = nextafter(x, y);
3814+
}
3815+
return PyFloat_FromDouble(x);
38063816
}
38073817

38083818

0 commit comments

Comments
 (0)