Skip to content

Commit a592361

Browse files
committed
gh-146238: support half-floats in the array module
1 parent f5dfb79 commit a592361

File tree

5 files changed

+88
-9
lines changed

5 files changed

+88
-9
lines changed

Doc/library/array.rst

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,15 @@ defined:
4242
+-----------+--------------------+-------------------+-----------------------+-------+
4343
| ``'Q'`` | unsigned long long | int | 8 | |
4444
+-----------+--------------------+-------------------+-----------------------+-------+
45+
| ``'e'`` | _Float16 | float | 2 | \(3) |
46+
+-----------+--------------------+-------------------+-----------------------+-------+
4547
| ``'f'`` | float | float | 4 | |
4648
+-----------+--------------------+-------------------+-----------------------+-------+
4749
| ``'d'`` | double | float | 8 | |
4850
+-----------+--------------------+-------------------+-----------------------+-------+
49-
| ``'F'`` | float complex | complex | 8 | \(3) |
51+
| ``'F'`` | float complex | complex | 8 | \(4) |
5052
+-----------+--------------------+-------------------+-----------------------+-------+
51-
| ``'D'`` | double complex | complex | 16 | \(3) |
53+
| ``'D'`` | double complex | complex | 16 | \(4) |
5254
+-----------+--------------------+-------------------+-----------------------+-------+
5355

5456

@@ -69,6 +71,15 @@ Notes:
6971
.. versionadded:: 3.13
7072

7173
(3)
74+
The IEEE 754 binary16 "half precision" type was introduced in the 2008
75+
revision of the `IEEE 754 standard <ieee 754 standard_>`_.
76+
This type is not widely supported by C compilers. It's available
77+
as :c:expr:`_Float16` type, if the compiler supports the Annex H
78+
of the C23 standard.
79+
80+
.. versionadded:: 3.15
81+
82+
(4)
7283
Complex types (``F`` and ``D``) are available unconditionally,
7384
regardless on support for complex types (the Annex G of the C11 standard)
7485
by the C compiler.
@@ -297,3 +308,5 @@ Examples::
297308

298309
`NumPy <https://numpy.org/>`_
299310
The NumPy package defines another array type.
311+
312+
.. _ieee 754 standard: https://en.wikipedia.org/wiki/IEEE_754-2008_revision

Doc/whatsnew/3.15.rst

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -632,9 +632,10 @@ argparse
632632
array
633633
-----
634634

635-
* Support the :c:expr:`float complex` and :c:expr:`double complex` C types
636-
(formatting characters ``'F'`` and ``'D'`` respectively).
637-
(Contributed by Sergey B Kirpichev in :gh:`146151`.)
635+
* Support half-floats (16-bit IEEE 754 binary interchange format, formatting
636+
character ``'e'``), the :c:expr:`float complex` and :c:expr:`double complex`
637+
C types (formatting characters ``'F'`` and ``'D'`` respectively).
638+
(Contributed by Sergey B Kirpichev in :gh:`146151` and :gh:`146238`.)
638639

639640

640641
base64

Lib/test/test_array.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class ArraySubclassWithKwargs(array.array):
3131
def __init__(self, typecode, newarg=None):
3232
array.array.__init__(self)
3333

34-
typecodes = 'uwbBhHiIlLfdqQFD'
34+
typecodes = 'uwbBhHiIlLfdqQFDe'
3535

3636
class MiscTest(unittest.TestCase):
3737

@@ -143,7 +143,7 @@ def test_error(self):
143143
self.assertRaises(ValueError, array_reconstructor,
144144
array.array, "b", UNKNOWN_FORMAT, b"")
145145
self.assertRaises(ValueError, array_reconstructor,
146-
array.array, "b", 26, b"")
146+
array.array, "b", 28, b"")
147147
self.assertRaises(ValueError, array_reconstructor,
148148
array.array, "d", 16, b"a")
149149

@@ -1578,6 +1578,13 @@ def test_byteswap(self):
15781578
self.assertEqual(a, b)
15791579

15801580

1581+
class HalfFloatTest(FPTest, unittest.TestCase):
1582+
example = [-42.0, 0, 42, 1e3, -1e3]
1583+
smallerexample = [-42.0, 0, 42, 1e3, -2e3]
1584+
biggerexample = [-42.0, 0, 42, 1e3, 1e3]
1585+
typecode = 'e'
1586+
minitemsize = 2
1587+
15811588
class FloatTest(FPTest, unittest.TestCase):
15821589
typecode = 'f'
15831590
minitemsize = 4
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Support half-floats (type code ``'e'`` of the :mod:`struct` module) in the
2+
:mod:`array` module. Patch by Sergey B Kirpichev.

Modules/arraymodule.c

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,12 @@ enum machine_format_code {
122122
IEEE_754_FLOAT_COMPLEX_LE = 22,
123123
IEEE_754_FLOAT_COMPLEX_BE = 23,
124124
IEEE_754_DOUBLE_COMPLEX_LE = 24,
125-
IEEE_754_DOUBLE_COMPLEX_BE = 25
125+
IEEE_754_DOUBLE_COMPLEX_BE = 25,
126+
IEEE_754_FLOAT16_LE = 26,
127+
IEEE_754_FLOAT16_BE = 27
126128
};
127129
#define MACHINE_FORMAT_CODE_MIN 0
128-
#define MACHINE_FORMAT_CODE_MAX 25
130+
#define MACHINE_FORMAT_CODE_MAX 27
129131

130132

131133
/*
@@ -614,6 +616,32 @@ QQ_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
614616
return 0;
615617
}
616618

619+
static PyObject *
620+
e_getitem(arrayobject *ap, Py_ssize_t i)
621+
{
622+
double x = PyFloat_Unpack2(ap->ob_item + sizeof(short)*i,
623+
PY_LITTLE_ENDIAN);
624+
625+
return PyFloat_FromDouble(x);
626+
}
627+
628+
static int
629+
e_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
630+
{
631+
float x;
632+
if (!PyArg_Parse(v, "f;array item must be float", &x)) {
633+
return -1;
634+
}
635+
636+
CHECK_ARRAY_BOUNDS(ap, i);
637+
638+
if (i >= 0) {
639+
return PyFloat_Pack2(x, ap->ob_item + sizeof(short)*i,
640+
PY_LITTLE_ENDIAN);
641+
}
642+
return 0;
643+
}
644+
617645
static PyObject *
618646
f_getitem(arrayobject *ap, Py_ssize_t i)
619647
{
@@ -754,6 +782,7 @@ static const struct arraydescr descriptors[] = {
754782
{'L', sizeof(long), LL_getitem, LL_setitem, LL_compareitems, "L", 1, 0},
755783
{'q', sizeof(long long), q_getitem, q_setitem, q_compareitems, "q", 1, 1},
756784
{'Q', sizeof(long long), QQ_getitem, QQ_setitem, QQ_compareitems, "Q", 1, 0},
785+
{'e', sizeof(short), e_getitem, e_setitem, NULL, "e", 0, 0},
757786
{'f', sizeof(float), f_getitem, f_setitem, NULL, "f", 0, 0},
758787
{'d', sizeof(double), d_getitem, d_setitem, NULL, "d", 0, 0},
759788
{'F', 2*sizeof(float), cf_getitem, cf_setitem, NULL, "F", 0, 0},
@@ -2093,6 +2122,8 @@ static const struct mformatdescr {
20932122
{8, 0, 1}, /* 23: IEEE_754_FLOAT_COMPLEX_BE */
20942123
{16, 0, 0}, /* 24: IEEE_754_DOUBLE_COMPLEX_LE */
20952124
{16, 0, 1}, /* 25: IEEE_754_DOUBLE_COMPLEX_BE */
2125+
{2, 0, 0}, /* 26: IEEE_754_FLOAT16_LE */
2126+
{2, 0, 1} /* 27: IEEE_754_FLOAT16_BE */
20962127
};
20972128

20982129

@@ -2127,6 +2158,9 @@ typecode_to_mformat_code(char typecode)
21272158
case 'w':
21282159
return UTF32_LE + is_big_endian;
21292160

2161+
case 'e':
2162+
return _PY_FLOAT_BIG_ENDIAN ? IEEE_754_FLOAT16_BE : IEEE_754_FLOAT16_LE;
2163+
21302164
case 'f':
21312165
if (sizeof(float) == 4) {
21322166
const float y = 16711938.0;
@@ -2326,6 +2360,27 @@ array__array_reconstructor_impl(PyObject *module, PyTypeObject *arraytype,
23262360
return NULL;
23272361
}
23282362
switch (mformat_code) {
2363+
case IEEE_754_FLOAT16_LE:
2364+
case IEEE_754_FLOAT16_BE: {
2365+
Py_ssize_t i;
2366+
int le = (mformat_code == IEEE_754_FLOAT_LE) ? 1 : 0;
2367+
Py_ssize_t itemcount = Py_SIZE(items) / 2;
2368+
const char *memstr = PyBytes_AS_STRING(items);
2369+
2370+
converted_items = PyList_New(itemcount);
2371+
if (converted_items == NULL)
2372+
return NULL;
2373+
for (i = 0; i < itemcount; i++) {
2374+
PyObject *pyfloat = PyFloat_FromDouble(
2375+
PyFloat_Unpack2(&memstr[i * 2], le));
2376+
if (pyfloat == NULL) {
2377+
Py_DECREF(converted_items);
2378+
return NULL;
2379+
}
2380+
PyList_SET_ITEM(converted_items, i, pyfloat);
2381+
}
2382+
break;
2383+
}
23292384
case IEEE_754_FLOAT_LE:
23302385
case IEEE_754_FLOAT_BE: {
23312386
Py_ssize_t i;
@@ -3146,6 +3201,7 @@ The following type codes are defined:\n\
31463201
'L' unsigned integer 4\n\
31473202
'q' signed integer 8 (see note)\n\
31483203
'Q' unsigned integer 8 (see note)\n\
3204+
'e' 16-bit IEEE floats 2\n\
31493205
'f' floating-point 4\n\
31503206
'd' floating-point 8\n\
31513207
'F' float complex 8\n\

0 commit comments

Comments
 (0)