https://github.com/python/cpython/commit/e79fd603392b9946c671a688da91281f7fdb1e2a
commit: e79fd603392b9946c671a688da91281f7fdb1e2a
branch: main
author: Sergey B Kirpichev <[email protected]>
committer: vstinner <[email protected]>
date: 2026-03-30T19:48:34Z
summary:

gh-146238: Support half-floats in the array module (#146242)

Co-authored-by: Victor Stinner <[email protected]>

files:
A Misc/NEWS.d/next/Library/2026-03-21-10-02-20.gh-issue-146238.2WpMOj.rst
M Doc/library/array.rst
M Doc/whatsnew/3.15.rst
M Lib/test/test_array.py
M Modules/arraymodule.c

diff --git a/Doc/library/array.rst b/Doc/library/array.rst
index 6a69361369bdec..8b0a0c84548127 100644
--- a/Doc/library/array.rst
+++ b/Doc/library/array.rst
@@ -42,13 +42,15 @@ defined:
 
+-----------+--------------------+-------------------+-----------------------+-------+
 | ``'Q'``   | unsigned long long | int               | 8                     | 
      |
 
+-----------+--------------------+-------------------+-----------------------+-------+
+| ``'e'``   | _Float16           | float             | 2                     | 
\(3)  |
++-----------+--------------------+-------------------+-----------------------+-------+
 | ``'f'``   | float              | float             | 4                     | 
      |
 
+-----------+--------------------+-------------------+-----------------------+-------+
 | ``'d'``   | double             | float             | 8                     | 
      |
 
+-----------+--------------------+-------------------+-----------------------+-------+
-| ``'F'``   | float complex      | complex           | 8                     | 
\(3)  |
+| ``'F'``   | float complex      | complex           | 8                     | 
\(4)  |
 
+-----------+--------------------+-------------------+-----------------------+-------+
-| ``'D'``   | double complex     | complex           | 16                    | 
\(3)  |
+| ``'D'``   | double complex     | complex           | 16                    | 
\(4)  |
 
+-----------+--------------------+-------------------+-----------------------+-------+
 
 
@@ -69,6 +71,15 @@ Notes:
    .. versionadded:: 3.13
 
 (3)
+   The IEEE 754 binary16 "half precision" type was introduced in the 2008
+   revision of the `IEEE 754 standard <ieee 754 standard_>`_.
+   This type is not widely supported by C compilers.  It's available
+   as :c:expr:`_Float16` type, if the compiler supports the Annex H
+   of the C23 standard.
+
+   .. versionadded:: next
+
+(4)
    Complex types (``F`` and ``D``) are available unconditionally,
    regardless on support for complex types (the Annex G of the C11 standard)
    by the C compiler.
@@ -304,3 +315,5 @@ Examples::
 
    `NumPy <https://numpy.org/>`_
       The NumPy package defines another array type.
+
+.. _ieee 754 standard: https://en.wikipedia.org/wiki/IEEE_754-2008_revision
diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst
index 7d13eccb22311f..462482c80122ea 100644
--- a/Doc/whatsnew/3.15.rst
+++ b/Doc/whatsnew/3.15.rst
@@ -642,6 +642,10 @@ array
   formatting characters ``'F'`` and ``'D'`` respectively.
   (Contributed by Sergey B Kirpichev in :gh:`146151`.)
 
+* Support half-floats (16-bit IEEE 754 binary interchange format): formatting
+  character ``'e'``.
+  (Contributed by Sergey B Kirpichev in :gh:`146238`.)
+
 
 base64
 ------
diff --git a/Lib/test/test_array.py b/Lib/test/test_array.py
index b3561e7650a0ad..4493349798d9c8 100755
--- a/Lib/test/test_array.py
+++ b/Lib/test/test_array.py
@@ -31,7 +31,7 @@ class ArraySubclassWithKwargs(array.array):
     def __init__(self, typecode, newarg=None):
         array.array.__init__(self)
 
-typecodes = 'uwbBhHiIlLfdqQFD'
+typecodes = 'uwbBhHiIlLfdqQFDe'
 
 class MiscTest(unittest.TestCase):
 
@@ -117,8 +117,10 @@ def __index__(self):
 IEEE_754_FLOAT_COMPLEX_BE = 23
 IEEE_754_DOUBLE_COMPLEX_LE = 24
 IEEE_754_DOUBLE_COMPLEX_BE = 25
+IEEE_754_FLOAT16_LE = 26
+IEEE_754_FLOAT16_BE = 27
 
-MACHINE_FORMAT_CODE_MAX = 25
+MACHINE_FORMAT_CODE_MAX = 27
 
 
 class ArrayReconstructorTest(unittest.TestCase):
@@ -1588,6 +1590,13 @@ def test_byteswap(self):
             self.assertEqual(a, b)
 
 
+class HalfFloatTest(FPTest, unittest.TestCase):
+    example = [-42.0, 0, 42, 1e2, -1e4]
+    smallerexample = [-42.0, 0, 42, 1e2, -2e4]
+    biggerexample = [-42.0, 0, 42, 1e2, 1e4]
+    typecode = 'e'
+    minitemsize = 2
+
 class FloatTest(FPTest, unittest.TestCase):
     typecode = 'f'
     minitemsize = 4
diff --git 
a/Misc/NEWS.d/next/Library/2026-03-21-10-02-20.gh-issue-146238.2WpMOj.rst 
b/Misc/NEWS.d/next/Library/2026-03-21-10-02-20.gh-issue-146238.2WpMOj.rst
new file mode 100644
index 00000000000000..35e951e38e4152
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-03-21-10-02-20.gh-issue-146238.2WpMOj.rst
@@ -0,0 +1,2 @@
+Support half-floats (type code ``'e'`` of the :mod:`struct` module) in the
+:mod:`array` module.  Patch by Sergey B Kirpichev.
diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c
index 42572833c2128a..555a35b3fc92ab 100644
--- a/Modules/arraymodule.c
+++ b/Modules/arraymodule.c
@@ -119,10 +119,12 @@ enum machine_format_code {
     IEEE_754_FLOAT_COMPLEX_LE = 22,
     IEEE_754_FLOAT_COMPLEX_BE = 23,
     IEEE_754_DOUBLE_COMPLEX_LE = 24,
-    IEEE_754_DOUBLE_COMPLEX_BE = 25
+    IEEE_754_DOUBLE_COMPLEX_BE = 25,
+    IEEE_754_FLOAT16_LE = 26,
+    IEEE_754_FLOAT16_BE = 27
 };
 #define MACHINE_FORMAT_CODE_MIN 0
-#define MACHINE_FORMAT_CODE_MAX 25
+#define MACHINE_FORMAT_CODE_MAX 27
 
 
 /*
@@ -611,6 +613,32 @@ QQ_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
     return 0;
 }
 
+static PyObject *
+e_getitem(arrayobject *ap, Py_ssize_t i)
+{
+    double x = PyFloat_Unpack2(ap->ob_item + sizeof(short)*i,
+                               PY_LITTLE_ENDIAN);
+
+    return PyFloat_FromDouble(x);
+}
+
+static int
+e_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
+{
+    float x;
+    if (!PyArg_Parse(v, "f;array item must be float", &x)) {
+        return -1;
+    }
+
+    CHECK_ARRAY_BOUNDS(ap, i);
+
+    if (i >= 0) {
+        return PyFloat_Pack2(x, ap->ob_item + sizeof(short)*i,
+                             PY_LITTLE_ENDIAN);
+    }
+    return 0;
+}
+
 static PyObject *
 f_getitem(arrayobject *ap, Py_ssize_t i)
 {
@@ -751,6 +779,7 @@ static const struct arraydescr descriptors[] = {
     {'L', sizeof(long), LL_getitem, LL_setitem, LL_compareitems, "L", 1, 0},
     {'q', sizeof(long long), q_getitem, q_setitem, q_compareitems, "q", 1, 1},
     {'Q', sizeof(long long), QQ_getitem, QQ_setitem, QQ_compareitems, "Q", 1, 
0},
+    {'e', sizeof(short), e_getitem, e_setitem, NULL, "e", 0, 0},
     {'f', sizeof(float), f_getitem, f_setitem, NULL, "f", 0, 0},
     {'d', sizeof(double), d_getitem, d_setitem, NULL, "d", 0, 0},
     {'F', 2*sizeof(float), cf_getitem, cf_setitem, NULL, "F", 0, 0},
@@ -2090,6 +2119,8 @@ static const struct mformatdescr {
     {8, 0, 1},                  /* 23: IEEE_754_FLOAT_COMPLEX_BE */
     {16, 0, 0},                 /* 24: IEEE_754_DOUBLE_COMPLEX_LE */
     {16, 0, 1},                 /* 25: IEEE_754_DOUBLE_COMPLEX_BE */
+    {2, 0, 0},                  /* 26: IEEE_754_FLOAT16_LE */
+    {2, 0, 1}                   /* 27: IEEE_754_FLOAT16_BE */
 };
 
 
@@ -2124,6 +2155,9 @@ typecode_to_mformat_code(char typecode)
     case 'w':
         return UTF32_LE + is_big_endian;
 
+    case 'e':
+        return _PY_FLOAT_BIG_ENDIAN ? IEEE_754_FLOAT16_BE : 
IEEE_754_FLOAT16_LE;
+
     case 'f':
         return _PY_FLOAT_BIG_ENDIAN ? IEEE_754_FLOAT_BE : IEEE_754_FLOAT_LE;
 
@@ -2309,6 +2343,27 @@ array__array_reconstructor_impl(PyObject *module, 
PyTypeObject *arraytype,
         return NULL;
     }
     switch (mformat_code) {
+    case IEEE_754_FLOAT16_LE:
+    case IEEE_754_FLOAT16_BE: {
+        Py_ssize_t i;
+        int le = (mformat_code == IEEE_754_FLOAT_LE) ? 1 : 0;
+        Py_ssize_t itemcount = Py_SIZE(items) / 2;
+        const char *memstr = PyBytes_AS_STRING(items);
+
+        converted_items = PyList_New(itemcount);
+        if (converted_items == NULL)
+            return NULL;
+        for (i = 0; i < itemcount; i++) {
+            PyObject *pyfloat = PyFloat_FromDouble(
+                PyFloat_Unpack2(&memstr[i * 2], le));
+            if (pyfloat == NULL) {
+                Py_DECREF(converted_items);
+                return NULL;
+            }
+            PyList_SET_ITEM(converted_items, i, pyfloat);
+        }
+        break;
+    }
     case IEEE_754_FLOAT_LE:
     case IEEE_754_FLOAT_BE: {
         Py_ssize_t i;
@@ -3129,6 +3184,7 @@ The following type codes are defined:\n\
     'L'         unsigned integer   4\n\
     'q'         signed integer     8 (see note)\n\
     'Q'         unsigned integer   8 (see note)\n\
+    'e'         16-bit IEEE floats 2\n\
     'f'         floating-point     4\n\
     'd'         floating-point     8\n\
     'F'         float complex      8\n\

_______________________________________________
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