[Python-checkins] bpo-39288: Add math.nextafter(x, y) (GH-17937)

Victor Stinner webhook-mailer at python.org
Sat Jan 11 20:15:50 EST 2020


https://github.com/python/cpython/commit/100fafcf20e8fc67cd8ef512074f9c0a253cb427
commit: 100fafcf20e8fc67cd8ef512074f9c0a253cb427
branch: master
author: Victor Stinner <vstinner at python.org>
committer: GitHub <noreply at github.com>
date: 2020-01-12T02:15:42+01:00
summary:

bpo-39288: Add math.nextafter(x, y) (GH-17937)

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

files:
A Misc/NEWS.d/next/Library/2020-01-10-16-52-09.bpo-39288.IB-aQX.rst
M Doc/library/math.rst
M Doc/whatsnew/3.9.rst
M Lib/test/test_math.py
M Modules/clinic/mathmodule.c.h
M Modules/mathmodule.c

diff --git a/Doc/library/math.rst b/Doc/library/math.rst
index 43eaba935a164..135adf8f6362d 100644
--- a/Doc/library/math.rst
+++ b/Doc/library/math.rst
@@ -213,6 +213,14 @@ Number-theoretic and representation functions
    of *x* and are floats.
 
 
+.. function:: nextafter(x, y)
+
+   Return the next floating-point value after *x* towards *y*.
+
+   If *x* is equal to *y*, return *y*.
+
+   .. versionadded:: 3.9
+
 .. function:: perm(n, k=None)
 
    Return the number of ways to choose *k* items from *n* items
diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst
index 8cfb5725bb5e1..a686d640ae94b 100644
--- a/Doc/whatsnew/3.9.rst
+++ b/Doc/whatsnew/3.9.rst
@@ -177,6 +177,13 @@ with this change. The overridden methods of :class:`~imaplib.IMAP4_SSL` and
 :class:`~imaplib.IMAP4_stream` were applied to this change.
 (Contributed by Dong-hee Na in :issue:`38615`.)
 
+math
+----
+
+Add :func:`math.nextafter`: return the next floating-point value after *x*
+towards *y*.
+(Contributed by Victor Stinner in :issue:`39288`.)
+
 nntplib
 -------
 
diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py
index 5c35c8cff129b..b64fd41a5481c 100644
--- a/Lib/test/test_math.py
+++ b/Lib/test/test_math.py
@@ -2033,6 +2033,60 @@ def testComb(self):
             self.assertIs(type(comb(IntSubclass(5), IntSubclass(k))), int)
             self.assertIs(type(comb(MyIndexable(5), MyIndexable(k))), int)
 
+    def assertEqualSign(self, x, y):
+        """Similar to assertEqual(), but compare also the sign.
+
+        Function useful to check to signed zero.
+        """
+        self.assertEqual(x, y)
+        self.assertEqual(math.copysign(1.0, x), math.copysign(1.0, y))
+
+    @requires_IEEE_754
+    def test_nextafter(self):
+        # around 2^52 and 2^63
+        self.assertEqual(math.nextafter(4503599627370496.0, -INF),
+                         4503599627370495.5)
+        self.assertEqual(math.nextafter(4503599627370496.0, INF),
+                         4503599627370497.0)
+        self.assertEqual(math.nextafter(9223372036854775808.0, 0.0),
+                         9223372036854774784.0)
+        self.assertEqual(math.nextafter(-9223372036854775808.0, 0.0),
+                         -9223372036854774784.0)
+
+        # around 1.0
+        self.assertEqual(math.nextafter(1.0, -INF),
+                         float.fromhex('0x1.fffffffffffffp-1'))
+        self.assertEqual(math.nextafter(1.0, INF),
+                         float.fromhex('0x1.0000000000001p+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)
+
+        # around 0.0
+        smallest_subnormal = sys.float_info.min * sys.float_info.epsilon
+        self.assertEqual(math.nextafter(+0.0, INF), smallest_subnormal)
+        self.assertEqual(math.nextafter(-0.0, INF), smallest_subnormal)
+        self.assertEqual(math.nextafter(+0.0, -INF), -smallest_subnormal)
+        self.assertEqual(math.nextafter(-0.0, -INF), -smallest_subnormal)
+        self.assertEqualSign(math.nextafter(smallest_subnormal, +0.0), +0.0)
+        self.assertEqualSign(math.nextafter(-smallest_subnormal, +0.0), -0.0)
+        self.assertEqualSign(math.nextafter(smallest_subnormal, -0.0), +0.0)
+        self.assertEqualSign(math.nextafter(-smallest_subnormal, -0.0), -0.0)
+
+        # around infinity
+        largest_normal = sys.float_info.max
+        self.assertEqual(math.nextafter(INF, 0.0), largest_normal)
+        self.assertEqual(math.nextafter(-INF, 0.0), -largest_normal)
+        self.assertEqual(math.nextafter(largest_normal, INF), INF)
+        self.assertEqual(math.nextafter(-largest_normal, -INF), -INF)
+
+        # NaN
+        self.assertTrue(math.isnan(math.nextafter(NAN, 1.0)))
+        self.assertTrue(math.isnan(math.nextafter(1.0, NAN)))
+        self.assertTrue(math.isnan(math.nextafter(NAN, NAN)))
+
 
 def test_main():
     from doctest import DocFileSuite
diff --git a/Misc/NEWS.d/next/Library/2020-01-10-16-52-09.bpo-39288.IB-aQX.rst b/Misc/NEWS.d/next/Library/2020-01-10-16-52-09.bpo-39288.IB-aQX.rst
new file mode 100644
index 0000000000000..0e0ec99c344ae
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2020-01-10-16-52-09.bpo-39288.IB-aQX.rst
@@ -0,0 +1,2 @@
+Add :func:`math.nextafter`: return the next floating-point value after *x*
+towards *y*.
diff --git a/Modules/clinic/mathmodule.c.h b/Modules/clinic/mathmodule.c.h
index 95d68ee55ae5b..f34633cb0c021 100644
--- a/Modules/clinic/mathmodule.c.h
+++ b/Modules/clinic/mathmodule.c.h
@@ -808,4 +808,52 @@ math_comb(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
 exit:
     return return_value;
 }
-/*[clinic end generated code: output=9a2b3dc91eb9aadd input=a9049054013a1b77]*/
+
+PyDoc_STRVAR(math_nextafter__doc__,
+"nextafter($module, x, y, /)\n"
+"--\n"
+"\n"
+"Return the next floating-point value after x towards y.");
+
+#define MATH_NEXTAFTER_METHODDEF    \
+    {"nextafter", (PyCFunction)(void(*)(void))math_nextafter, METH_FASTCALL, math_nextafter__doc__},
+
+static PyObject *
+math_nextafter_impl(PyObject *module, double x, double y);
+
+static PyObject *
+math_nextafter(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
+{
+    PyObject *return_value = NULL;
+    double x;
+    double y;
+
+    if (!_PyArg_CheckPositional("nextafter", nargs, 2, 2)) {
+        goto exit;
+    }
+    if (PyFloat_CheckExact(args[0])) {
+        x = PyFloat_AS_DOUBLE(args[0]);
+    }
+    else
+    {
+        x = PyFloat_AsDouble(args[0]);
+        if (x == -1.0 && PyErr_Occurred()) {
+            goto exit;
+        }
+    }
+    if (PyFloat_CheckExact(args[1])) {
+        y = PyFloat_AS_DOUBLE(args[1]);
+    }
+    else
+    {
+        y = PyFloat_AsDouble(args[1]);
+        if (y == -1.0 && PyErr_Occurred()) {
+            goto exit;
+        }
+    }
+    return_value = math_nextafter_impl(module, x, y);
+
+exit:
+    return return_value;
+}
+/*[clinic end generated code: output=e4ed1a800e4b2eae input=a9049054013a1b77]*/
diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c
index e60e19bc49095..632a421e3bbe5 100644
--- a/Modules/mathmodule.c
+++ b/Modules/mathmodule.c
@@ -3295,6 +3295,25 @@ math_comb_impl(PyObject *module, PyObject *n, PyObject *k)
 }
 
 
+/*[clinic input]
+math.nextafter
+
+    x: double
+    y: double
+    /
+
+Return the next floating-point value 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]*/
+{
+    double f = nextafter(x, y);
+    return PyFloat_FromDouble(f);
+}
+
+
 static PyMethodDef math_methods[] = {
     {"acos",            math_acos,      METH_O,         math_acos_doc},
     {"acosh",           math_acosh,     METH_O,         math_acosh_doc},
@@ -3346,6 +3365,7 @@ static PyMethodDef math_methods[] = {
     MATH_PROD_METHODDEF
     MATH_PERM_METHODDEF
     MATH_COMB_METHODDEF
+    MATH_NEXTAFTER_METHODDEF
     {NULL,              NULL}           /* sentinel */
 };
 



More information about the Python-checkins mailing list