Skip to content

gh-135853: add math.signbit #135877

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
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
10 changes: 10 additions & 0 deletions Doc/library/math.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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**
Expand Down Expand Up @@ -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*:
Expand Down
3 changes: 3 additions & 0 deletions Doc/whatsnew/3.15.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
-------
Expand Down
15 changes: 13 additions & 2 deletions Lib/test/test_math.py
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
36 changes: 35 additions & 1 deletion Modules/clinic/mathmodule.c.h

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

18 changes: 18 additions & 0 deletions Modules/mathmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -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).",
Expand Down Expand Up @@ -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},
Expand Down
Loading