gh-132908: Add math.isnormal/issubnormal() functions (GH132935)

This commit is contained in:
Sergey B Kirpichev 2025-06-02 13:38:05 +03:00 committed by GitHub
parent 128195e12e
commit 5f61cde80a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 161 additions and 2 deletions

View File

@ -53,6 +53,8 @@ noted otherwise, all return values are floats.
:func:`frexp(x) <frexp>` Mantissa and exponent of *x* :func:`frexp(x) <frexp>` Mantissa and exponent of *x*
:func:`isclose(a, b, rel_tol, abs_tol) <isclose>` Check if the values *a* and *b* are close to each other :func:`isclose(a, b, rel_tol, abs_tol) <isclose>` Check if the values *a* and *b* are close to each other
:func:`isfinite(x) <isfinite>` Check if *x* is neither an infinity nor a NaN :func:`isfinite(x) <isfinite>` Check if *x* is neither an infinity nor a NaN
:func:`isnormal(x) <isnormal>` Check if *x* is a normal number
:func:`issubnormal(x) <issubnormal>` Check if *x* is a subnormal number
:func:`isinf(x) <isinf>` Check if *x* is a positive or negative infinity :func:`isinf(x) <isinf>` Check if *x* is a positive or negative infinity
:func:`isnan(x) <isnan>` Check if *x* is a NaN (not a number) :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:`ldexp(x, i) <ldexp>` ``x * (2**i)``, inverse of function :func:`frexp`
@ -373,6 +375,24 @@ Floating point manipulation functions
.. versionadded:: 3.2 .. versionadded:: 3.2
.. function:: isnormal(x)
Return ``True`` if *x* is a normal number, that is a finite
nonzero number that is not a subnormal (see :func:`issubnormal`).
Return ``False`` otherwise.
.. versionadded:: next
.. function:: issubnormal(x)
Return ``True`` if *x* is a subnormal number, that is a finite
nonzero number with a magnitude smaller than the smallest positive normal
number, see :data:`sys.float_info.min`. Return ``False`` otherwise.
.. versionadded:: next
.. function:: isinf(x) .. function:: isinf(x)
Return ``True`` if *x* is a positive or negative infinity, and Return ``True`` if *x* is a positive or negative infinity, and

View File

@ -1454,7 +1454,7 @@ math
---- ----
* Added more detailed error messages for domain errors in the module. * Added more detailed error messages for domain errors in the module.
(Contributed by by Charlie Zhao and Sergey B Kirpichev in :gh:`101410`.) (Contributed by Charlie Zhao and Sergey B Kirpichev in :gh:`101410`.)
mimetypes mimetypes

View File

@ -105,6 +105,13 @@ difflib
(Contributed by Jiahao Li in :gh:`134580`.) (Contributed by Jiahao Li in :gh:`134580`.)
math
----
* Add :func:`math.isnormal` and :func:`math.issubnormal` functions.
(Contributed by Sergey B Kirpichev in :gh:`132908`.)
shelve shelve
------ ------

View File

@ -1973,6 +1973,28 @@ class MathTests(unittest.TestCase):
self.assertFalse(math.isfinite(float("inf"))) self.assertFalse(math.isfinite(float("inf")))
self.assertFalse(math.isfinite(float("-inf"))) self.assertFalse(math.isfinite(float("-inf")))
def testIsnormal(self):
self.assertTrue(math.isnormal(1.25))
self.assertTrue(math.isnormal(-1.0))
self.assertFalse(math.isnormal(0.0))
self.assertFalse(math.isnormal(-0.0))
self.assertFalse(math.isnormal(INF))
self.assertFalse(math.isnormal(NINF))
self.assertFalse(math.isnormal(NAN))
self.assertFalse(math.isnormal(FLOAT_MIN/2))
self.assertFalse(math.isnormal(-FLOAT_MIN/2))
def testIssubnormal(self):
self.assertFalse(math.issubnormal(1.25))
self.assertFalse(math.issubnormal(-1.0))
self.assertFalse(math.issubnormal(0.0))
self.assertFalse(math.issubnormal(-0.0))
self.assertFalse(math.issubnormal(INF))
self.assertFalse(math.issubnormal(NINF))
self.assertFalse(math.issubnormal(NAN))
self.assertTrue(math.issubnormal(FLOAT_MIN/2))
self.assertTrue(math.issubnormal(-FLOAT_MIN/2))
def testIsnan(self): def testIsnan(self):
self.assertTrue(math.isnan(float("nan"))) self.assertTrue(math.isnan(float("nan")))
self.assertTrue(math.isnan(float("-nan"))) self.assertTrue(math.isnan(float("-nan")))

View File

@ -0,0 +1,2 @@
Add :func:`math.isnormal` and :func:`math.issubnormal` functions. Patch by
Sergey B Kirpichev.

View File

@ -628,6 +628,74 @@ exit:
return return_value; return return_value;
} }
PyDoc_STRVAR(math_isnormal__doc__,
"isnormal($module, x, /)\n"
"--\n"
"\n"
"Return True if x is normal, and False otherwise.");
#define MATH_ISNORMAL_METHODDEF \
{"isnormal", (PyCFunction)math_isnormal, METH_O, math_isnormal__doc__},
static PyObject *
math_isnormal_impl(PyObject *module, double x);
static PyObject *
math_isnormal(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_isnormal_impl(module, x);
exit:
return return_value;
}
PyDoc_STRVAR(math_issubnormal__doc__,
"issubnormal($module, x, /)\n"
"--\n"
"\n"
"Return True if x is subnormal, and False otherwise.");
#define MATH_ISSUBNORMAL_METHODDEF \
{"issubnormal", (PyCFunction)math_issubnormal, METH_O, math_issubnormal__doc__},
static PyObject *
math_issubnormal_impl(PyObject *module, double x);
static PyObject *
math_issubnormal(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_issubnormal_impl(module, x);
exit:
return return_value;
}
PyDoc_STRVAR(math_isnan__doc__, PyDoc_STRVAR(math_isnan__doc__,
"isnan($module, x, /)\n" "isnan($module, x, /)\n"
"--\n" "--\n"
@ -1110,4 +1178,4 @@ math_ulp(PyObject *module, PyObject *arg)
exit: exit:
return return_value; return return_value;
} }
/*[clinic end generated code: output=77e7b8c161c39843 input=a9049054013a1b77]*/ /*[clinic end generated code: output=44bba3a0a052a364 input=a9049054013a1b77]*/

View File

@ -3118,6 +3118,44 @@ math_isfinite_impl(PyObject *module, double x)
} }
/*[clinic input]
math.isnormal
x: double
/
Return True if x is normal, and False otherwise.
[clinic start generated code]*/
static PyObject *
math_isnormal_impl(PyObject *module, double x)
/*[clinic end generated code: output=c7b302b5b89c3541 input=fdaa00c58aa7bc17]*/
{
return PyBool_FromLong(isnormal(x));
}
/*[clinic input]
math.issubnormal
x: double
/
Return True if x is subnormal, and False otherwise.
[clinic start generated code]*/
static PyObject *
math_issubnormal_impl(PyObject *module, double x)
/*[clinic end generated code: output=4e76ac98ddcae761 input=9a20aba7107d0d95]*/
{
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L
return PyBool_FromLong(issubnormal(x));
#else
return PyBool_FromLong(isfinite(x) && x && !isnormal(x));
#endif
}
/*[clinic input] /*[clinic input]
math.isnan math.isnan
@ -4145,6 +4183,8 @@ static PyMethodDef math_methods[] = {
MATH_HYPOT_METHODDEF MATH_HYPOT_METHODDEF
MATH_ISCLOSE_METHODDEF MATH_ISCLOSE_METHODDEF
MATH_ISFINITE_METHODDEF MATH_ISFINITE_METHODDEF
MATH_ISNORMAL_METHODDEF
MATH_ISSUBNORMAL_METHODDEF
MATH_ISINF_METHODDEF MATH_ISINF_METHODDEF
MATH_ISNAN_METHODDEF MATH_ISNAN_METHODDEF
MATH_ISQRT_METHODDEF MATH_ISQRT_METHODDEF