https://github.com/python/cpython/commit/5f61cde80a9b33c8e118b1c009fe2aaa4bb87356
commit: 5f61cde80a9b33c8e118b1c009fe2aaa4bb87356
branch: main
author: Sergey B Kirpichev <skirpic...@gmail.com>
committer: serhiy-storchaka <storch...@gmail.com>
date: 2025-06-02T13:38:05+03:00
summary:

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

files:
A Misc/NEWS.d/next/Library/2025-04-25-16-06-53.gh-issue-132908.wV5rja.rst
M Doc/library/math.rst
M Doc/whatsnew/3.14.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 11d3b756e21322..c8061fb16380cd 100644
--- a/Doc/library/math.rst
+++ b/Doc/library/math.rst
@@ -53,6 +53,8 @@ noted otherwise, all return values are floats.
 :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:`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:`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`
@@ -373,6 +375,24 @@ Floating point manipulation functions
    .. 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)
 
    Return ``True`` if *x* is a positive or negative infinity, and
diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst
index 561d1a8914b50c..27dfc75c90fbe9 100644
--- a/Doc/whatsnew/3.14.rst
+++ b/Doc/whatsnew/3.14.rst
@@ -1454,7 +1454,7 @@ math
 ----
 
 * 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
diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst
index b342939f70577f..a27a17afdba2a8 100644
--- a/Doc/whatsnew/3.15.rst
+++ b/Doc/whatsnew/3.15.rst
@@ -105,6 +105,13 @@ difflib
   (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
 ------
 
diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py
index d14336f8bac498..384ad5c828d9b3 100644
--- a/Lib/test/test_math.py
+++ b/Lib/test/test_math.py
@@ -1973,6 +1973,28 @@ def testIsfinite(self):
         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):
         self.assertTrue(math.isnan(float("nan")))
         self.assertTrue(math.isnan(float("-nan")))
diff --git 
a/Misc/NEWS.d/next/Library/2025-04-25-16-06-53.gh-issue-132908.wV5rja.rst 
b/Misc/NEWS.d/next/Library/2025-04-25-16-06-53.gh-issue-132908.wV5rja.rst
new file mode 100644
index 00000000000000..e33b061bb9ba1f
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-04-25-16-06-53.gh-issue-132908.wV5rja.rst
@@ -0,0 +1,2 @@
+Add :func:`math.isnormal` and :func:`math.issubnormal` functions.  Patch by
+Sergey B Kirpichev.
diff --git a/Modules/clinic/mathmodule.c.h b/Modules/clinic/mathmodule.c.h
index 9df73b187bb827..fbb012fb6dd9e1 100644
--- a/Modules/clinic/mathmodule.c.h
+++ b/Modules/clinic/mathmodule.c.h
@@ -628,6 +628,74 @@ math_isfinite(PyObject *module, PyObject *arg)
     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__,
 "isnan($module, x, /)\n"
 "--\n"
@@ -1110,4 +1178,4 @@ math_ulp(PyObject *module, PyObject *arg)
 exit:
     return return_value;
 }
-/*[clinic end generated code: output=77e7b8c161c39843 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=44bba3a0a052a364 input=a9049054013a1b77]*/
diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c
index 71d9c1387f5780..bbbb49115681de 100644
--- a/Modules/mathmodule.c
+++ b/Modules/mathmodule.c
@@ -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]
 math.isnan
 
@@ -4145,6 +4183,8 @@ static PyMethodDef math_methods[] = {
     MATH_HYPOT_METHODDEF
     MATH_ISCLOSE_METHODDEF
     MATH_ISFINITE_METHODDEF
+    MATH_ISNORMAL_METHODDEF
+    MATH_ISSUBNORMAL_METHODDEF
     MATH_ISINF_METHODDEF
     MATH_ISNAN_METHODDEF
     MATH_ISQRT_METHODDEF

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: arch...@mail-archive.com

Reply via email to