https://github.com/python/cpython/commit/42ccac2d7f29f6befed46ca7a131de82901dcbcf
commit: 42ccac2d7f29f6befed46ca7a131de82901dcbcf
branch: main
author: Bénédikt Tran <[email protected]>
committer: picnixz <[email protected]>
date: 2025-06-28T14:46:07+02:00
summary:

gh-135853: add `math.signbit` (#135877)

files:
A Misc/NEWS.d/next/Library/2025-06-24-10-23-37.gh-issue-135853.6xDNOG.rst
M Doc/library/math.rst
M Doc/whatsnew/3.15.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 ecb1d4102cac31..03da0e4713c42d 100644
--- a/Doc/library/math.rst
+++ b/Doc/library/math.rst
@@ -59,6 +59,7 @@ noted otherwise, all return values are floats.
 :func:`isnan(x) <isnan>`                              Check if *x* is a NaN  
(not a number)
 :func:`ldexp(x, i) <ldexp>`                           ``x * (2**i)``, inverse 
of function :func:`frexp`
 :func:`nextafter(x, y, steps) <nextafter>`            Floating-point value 
*steps* steps after *x* towards *y*
+:func:`signbit(x) <signbit>`                          Check if *x* is a 
negative number
 :func:`ulp(x) <ulp>`                                  Value of the least 
significant bit of *x*
 
 **Power, exponential and logarithmic functions**
@@ -431,6 +432,15 @@ Floating point manipulation functions
       Added the *steps* argument.
 
 
+.. function:: signbit(x)
+
+   Return ``True`` if the sign of *x* is negative and ``False`` otherwise.
+
+   This is useful to detect the sign bit of zeroes, infinities and NaNs.
+
+   .. versionadded:: next
+
+
 .. function:: ulp(x)
 
    Return the value of the least significant bit of the float *x*:
diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst
index 9f327cf904da1b..e58b4250f356c2 100644
--- a/Doc/whatsnew/3.15.rst
+++ b/Doc/whatsnew/3.15.rst
@@ -115,6 +115,9 @@ math
 * Add :func:`math.isnormal` and :func:`math.issubnormal` functions.
   (Contributed by Sergey B Kirpichev in :gh:`132908`.)
 
+* Add :func:`math.signbit` function.
+  (Contributed by Bénédikt Tran in :gh:`135853`.)
+
 
 os.path
 -------
diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py
index 384ad5c828d9b3..46cb54647b1968 100644
--- a/Lib/test/test_math.py
+++ b/Lib/test/test_math.py
@@ -475,6 +475,19 @@ def testCopysign(self):
         # similarly, copysign(2., NAN) could be 2. or -2.
         self.assertEqual(abs(math.copysign(2., NAN)), 2.)
 
+    def test_signbit(self):
+        self.assertRaises(TypeError, math.signbit)
+        self.assertRaises(TypeError, math.signbit, '1.0')
+
+        # C11, §7.12.3.6 requires signbit() to return a nonzero value
+        # if and only if the sign of its argument value is negative,
+        # but in practice, we are only interested in a boolean value.
+        self.assertIsInstance(math.signbit(1.0), bool)
+
+        for arg in [0., 1., INF, NAN]:
+            self.assertFalse(math.signbit(arg))
+            self.assertTrue(math.signbit(-arg))
+
     def testCos(self):
         self.assertRaises(TypeError, math.cos)
         self.ftest('cos(-pi/2)', math.cos(-math.pi/2), 0, abs_tol=math.ulp(1))
@@ -1387,7 +1400,6 @@ def __rmul__(self, other):
         args = ((-5, -5, 10), (1.5, 4611686018427387904, 2305843009213693952))
         self.assertEqual(sumprod(*args), 0.0)
 
-
     @requires_IEEE_754
     @unittest.skipIf(HAVE_DOUBLE_ROUNDING,
                          "sumprod() accuracy not guaranteed on machines with 
double rounding")
@@ -2486,7 +2498,6 @@ def test_nextafter(self):
         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)
diff --git 
a/Misc/NEWS.d/next/Library/2025-06-24-10-23-37.gh-issue-135853.6xDNOG.rst 
b/Misc/NEWS.d/next/Library/2025-06-24-10-23-37.gh-issue-135853.6xDNOG.rst
new file mode 100644
index 00000000000000..3fea3bc3e7cfe8
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-06-24-10-23-37.gh-issue-135853.6xDNOG.rst
@@ -0,0 +1,2 @@
+:mod:`math`: expose C99 :func:`~math.signbit` function to determine whether
+the sign bit of a floating-point value is set. Patch by Bénédikt Tran.
diff --git a/Modules/clinic/mathmodule.c.h b/Modules/clinic/mathmodule.c.h
index fbb012fb6dd9e1..a443c48faaa88a 100644
--- a/Modules/clinic/mathmodule.c.h
+++ b/Modules/clinic/mathmodule.c.h
@@ -84,6 +84,40 @@ PyDoc_STRVAR(math_floor__doc__,
 #define MATH_FLOOR_METHODDEF    \
     {"floor", (PyCFunction)math_floor, METH_O, math_floor__doc__},
 
+PyDoc_STRVAR(math_signbit__doc__,
+"signbit($module, x, /)\n"
+"--\n"
+"\n"
+"Return True if the sign of x is negative and False otherwise.");
+
+#define MATH_SIGNBIT_METHODDEF    \
+    {"signbit", (PyCFunction)math_signbit, METH_O, math_signbit__doc__},
+
+static PyObject *
+math_signbit_impl(PyObject *module, double x);
+
+static PyObject *
+math_signbit(PyObject *module, PyObject *arg)
+{
+    PyObject *return_value = NULL;
+    double x;
+
+    if (PyFloat_CheckExact(arg)) {
+        x = PyFloat_AS_DOUBLE(arg);
+    }
+    else
+    {
+        x = PyFloat_AsDouble(arg);
+        if (x == -1.0 && PyErr_Occurred()) {
+            goto exit;
+        }
+    }
+    return_value = math_signbit_impl(module, x);
+
+exit:
+    return return_value;
+}
+
 PyDoc_STRVAR(math_fsum__doc__,
 "fsum($module, seq, /)\n"
 "--\n"
@@ -1178,4 +1212,4 @@ math_ulp(PyObject *module, PyObject *arg)
 exit:
     return return_value;
 }
-/*[clinic end generated code: output=44bba3a0a052a364 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=4e3fa94d026f027b input=a9049054013a1b77]*/
diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c
index 1837de4735c176..033de0b2907a69 100644
--- a/Modules/mathmodule.c
+++ b/Modules/mathmodule.c
@@ -1233,6 +1233,23 @@ FUNC2(remainder, m_remainder,
       "Return x - n*y where n*y is the closest integer multiple of y.\n"
       "In the case where x is exactly halfway between two multiples of\n"
       "y, the nearest even value of n is used. The result is always exact.")
+
+/*[clinic input]
+math.signbit
+
+    x: double
+    /
+
+Return True if the sign of x is negative and False otherwise.
+[clinic start generated code]*/
+
+static PyObject *
+math_signbit_impl(PyObject *module, double x)
+/*[clinic end generated code: output=20c5f20156a9b871 input=3d3493fbcb5bdb3e]*/
+{
+    return PyBool_FromLong(signbit(x));
+}
+
 FUNC1D(sin, sin, 0,
       "sin($module, x, /)\n--\n\n"
       "Return the sine of x (measured in radians).",
@@ -4199,6 +4216,7 @@ static PyMethodDef math_methods[] = {
     MATH_POW_METHODDEF
     MATH_RADIANS_METHODDEF
     {"remainder",       _PyCFunction_CAST(math_remainder), METH_FASTCALL,  
math_remainder_doc},
+    MATH_SIGNBIT_METHODDEF
     {"sin",             math_sin,       METH_O,         math_sin_doc},
     {"sinh",            math_sinh,      METH_O,         math_sinh_doc},
     {"sqrt",            math_sqrt,      METH_O,         math_sqrt_doc},

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to