mirror of
https://github.com/python/cpython.git
synced 2026-06-06 01:41:04 +00:00
gh-146238: Support half-floats in the array module (#146242)
Co-authored-by: Victor Stinner <vstinner@python.org>
This commit is contained in:
parent
ec2bc12e54
commit
e79fd60339
5 changed files with 90 additions and 6 deletions
|
|
@ -42,13 +42,15 @@ defined:
|
||||||
+-----------+--------------------+-------------------+-----------------------+-------+
|
+-----------+--------------------+-------------------+-----------------------+-------+
|
||||||
| ``'Q'`` | unsigned long long | int | 8 | |
|
| ``'Q'`` | unsigned long long | int | 8 | |
|
||||||
+-----------+--------------------+-------------------+-----------------------+-------+
|
+-----------+--------------------+-------------------+-----------------------+-------+
|
||||||
|
| ``'e'`` | _Float16 | float | 2 | \(3) |
|
||||||
|
+-----------+--------------------+-------------------+-----------------------+-------+
|
||||||
| ``'f'`` | float | float | 4 | |
|
| ``'f'`` | float | float | 4 | |
|
||||||
+-----------+--------------------+-------------------+-----------------------+-------+
|
+-----------+--------------------+-------------------+-----------------------+-------+
|
||||||
| ``'d'`` | double | float | 8 | |
|
| ``'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
|
.. versionadded:: 3.13
|
||||||
|
|
||||||
(3)
|
(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,
|
Complex types (``F`` and ``D``) are available unconditionally,
|
||||||
regardless on support for complex types (the Annex G of the C11 standard)
|
regardless on support for complex types (the Annex G of the C11 standard)
|
||||||
by the C compiler.
|
by the C compiler.
|
||||||
|
|
@ -304,3 +315,5 @@ Examples::
|
||||||
|
|
||||||
`NumPy <https://numpy.org/>`_
|
`NumPy <https://numpy.org/>`_
|
||||||
The NumPy package defines another array type.
|
The NumPy package defines another array type.
|
||||||
|
|
||||||
|
.. _ieee 754 standard: https://en.wikipedia.org/wiki/IEEE_754-2008_revision
|
||||||
|
|
|
||||||
|
|
@ -642,6 +642,10 @@ array
|
||||||
formatting characters ``'F'`` and ``'D'`` respectively.
|
formatting characters ``'F'`` and ``'D'`` respectively.
|
||||||
(Contributed by Sergey B Kirpichev in :gh:`146151`.)
|
(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
|
base64
|
||||||
------
|
------
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ class ArraySubclassWithKwargs(array.array):
|
||||||
def __init__(self, typecode, newarg=None):
|
def __init__(self, typecode, newarg=None):
|
||||||
array.array.__init__(self)
|
array.array.__init__(self)
|
||||||
|
|
||||||
typecodes = 'uwbBhHiIlLfdqQFD'
|
typecodes = 'uwbBhHiIlLfdqQFDe'
|
||||||
|
|
||||||
class MiscTest(unittest.TestCase):
|
class MiscTest(unittest.TestCase):
|
||||||
|
|
||||||
|
|
@ -117,8 +117,10 @@ def __index__(self):
|
||||||
IEEE_754_FLOAT_COMPLEX_BE = 23
|
IEEE_754_FLOAT_COMPLEX_BE = 23
|
||||||
IEEE_754_DOUBLE_COMPLEX_LE = 24
|
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
|
||||||
|
|
||||||
MACHINE_FORMAT_CODE_MAX = 25
|
MACHINE_FORMAT_CODE_MAX = 27
|
||||||
|
|
||||||
|
|
||||||
class ArrayReconstructorTest(unittest.TestCase):
|
class ArrayReconstructorTest(unittest.TestCase):
|
||||||
|
|
@ -1588,6 +1590,13 @@ def test_byteswap(self):
|
||||||
self.assertEqual(a, b)
|
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):
|
class FloatTest(FPTest, unittest.TestCase):
|
||||||
typecode = 'f'
|
typecode = 'f'
|
||||||
minitemsize = 4
|
minitemsize = 4
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -119,10 +119,12 @@ enum machine_format_code {
|
||||||
IEEE_754_FLOAT_COMPLEX_LE = 22,
|
IEEE_754_FLOAT_COMPLEX_LE = 22,
|
||||||
IEEE_754_FLOAT_COMPLEX_BE = 23,
|
IEEE_754_FLOAT_COMPLEX_BE = 23,
|
||||||
IEEE_754_DOUBLE_COMPLEX_LE = 24,
|
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_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;
|
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 *
|
static PyObject *
|
||||||
f_getitem(arrayobject *ap, Py_ssize_t i)
|
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},
|
{'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), q_getitem, q_setitem, q_compareitems, "q", 1, 1},
|
||||||
{'Q', sizeof(long long), QQ_getitem, QQ_setitem, QQ_compareitems, "Q", 1, 0},
|
{'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},
|
{'f', sizeof(float), f_getitem, f_setitem, NULL, "f", 0, 0},
|
||||||
{'d', sizeof(double), d_getitem, d_setitem, NULL, "d", 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},
|
{'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 */
|
{8, 0, 1}, /* 23: IEEE_754_FLOAT_COMPLEX_BE */
|
||||||
{16, 0, 0}, /* 24: IEEE_754_DOUBLE_COMPLEX_LE */
|
{16, 0, 0}, /* 24: IEEE_754_DOUBLE_COMPLEX_LE */
|
||||||
{16, 0, 1}, /* 25: IEEE_754_DOUBLE_COMPLEX_BE */
|
{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':
|
case 'w':
|
||||||
return UTF32_LE + is_big_endian;
|
return UTF32_LE + is_big_endian;
|
||||||
|
|
||||||
|
case 'e':
|
||||||
|
return _PY_FLOAT_BIG_ENDIAN ? IEEE_754_FLOAT16_BE : IEEE_754_FLOAT16_LE;
|
||||||
|
|
||||||
case 'f':
|
case 'f':
|
||||||
return _PY_FLOAT_BIG_ENDIAN ? IEEE_754_FLOAT_BE : IEEE_754_FLOAT_LE;
|
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;
|
return NULL;
|
||||||
}
|
}
|
||||||
switch (mformat_code) {
|
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_LE:
|
||||||
case IEEE_754_FLOAT_BE: {
|
case IEEE_754_FLOAT_BE: {
|
||||||
Py_ssize_t i;
|
Py_ssize_t i;
|
||||||
|
|
@ -3129,6 +3184,7 @@ The following type codes are defined:\n\
|
||||||
'L' unsigned integer 4\n\
|
'L' unsigned integer 4\n\
|
||||||
'q' signed integer 8 (see note)\n\
|
'q' signed integer 8 (see note)\n\
|
||||||
'Q' unsigned integer 8 (see note)\n\
|
'Q' unsigned integer 8 (see note)\n\
|
||||||
|
'e' 16-bit IEEE floats 2\n\
|
||||||
'f' floating-point 4\n\
|
'f' floating-point 4\n\
|
||||||
'd' floating-point 8\n\
|
'd' floating-point 8\n\
|
||||||
'F' float complex 8\n\
|
'F' float complex 8\n\
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue