--------------
This module defines an object type which can compactly represent an array of
-basic values: characters, integers, floating-point numbers. Arrays are mutable :term:`sequence`
+basic values: characters, integers, floating-point numbers, complex numbers. Arrays are mutable :term:`sequence`
types and behave very much like lists, except that the type of objects stored in
them is constrained. The type is specified at object creation time by using a
:dfn:`type code`, which is a single character. The following type codes are
+-----------+--------------------+-------------------+-----------------------+-------+
| ``'d'`` | double | float | 8 | |
+-----------+--------------------+-------------------+-----------------------+-------+
+| ``'F'`` | float complex | complex | 8 | \(3) |
++-----------+--------------------+-------------------+-----------------------+-------+
+| ``'D'`` | double complex | complex | 16 | \(3) |
++-----------+--------------------+-------------------+-----------------------+-------+
+
Notes:
(2)
.. versionadded:: 3.13
+(3)
+ 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.
+ As specified in the C11 standard, each complex type is represented by a
+ two-element C array containing, respectively, the real and imaginary parts.
+
+ .. versionadded:: 3.15
+
.. seealso::
The :ref:`ctypes <ctypes-fundamental-data-types>` and
.. method:: byteswap()
"Byteswap" all items of the array. This is only supported for values which are
- 1, 2, 4, or 8 bytes in size; for other types of values, :exc:`RuntimeError` is
+ 1, 2, 4, 8 or 16 bytes in size; for other types of values, :exc:`RuntimeError` is
raised. It is useful when reading data from a file written on a machine with a
- different byte order.
+ different byte order. Note, that for complex types the order of
+ components (the real part, followed by imaginary part) is preserved.
.. method:: count(x)
(Contributed by Savannah Ostrowski in :gh:`142390`.)
+array
+-----
+
+* Support the :c:expr:`float complex` and :c:expr:`double complex` C types:
+ formatting characters ``'F'`` and ``'D'`` respectively.
+ (Contributed by Sergey B Kirpichev in :gh:`146151`.)
+
+
base64
------
def __init__(self, typecode, newarg=None):
array.array.__init__(self)
-typecodes = 'uwbBhHiIlLfdqQ'
+typecodes = 'uwbBhHiIlLfdqQFD'
class MiscTest(unittest.TestCase):
UTF16_BE = 19
UTF32_LE = 20
UTF32_BE = 21
+IEEE_754_FLOAT_COMPLEX_LE = 22
+IEEE_754_FLOAT_COMPLEX_BE = 23
+IEEE_754_DOUBLE_COMPLEX_LE = 24
+IEEE_754_DOUBLE_COMPLEX_BE = 25
+
+MACHINE_FORMAT_CODE_MAX = 25
class ArrayReconstructorTest(unittest.TestCase):
self.assertRaises(ValueError, array_reconstructor,
array.array, "b", UNKNOWN_FORMAT, b"")
self.assertRaises(ValueError, array_reconstructor,
- array.array, "b", 22, b"")
+ array.array, "b", MACHINE_FORMAT_CODE_MAX + 1, b"")
self.assertRaises(ValueError, array_reconstructor,
array.array, "d", 16, b"a")
(['d'], IEEE_754_DOUBLE_LE, '<dddd',
[9006104071832581.0, float('inf'), float('-inf'), -0.0]),
(['d'], IEEE_754_DOUBLE_BE, '>dddd',
- [9006104071832581.0, float('inf'), float('-inf'), -0.0])
+ [9006104071832581.0, float('inf'), float('-inf'), -0.0]),
+ (['F'], IEEE_754_FLOAT_COMPLEX_LE, '<FFFF',
+ [16711938.0j, float('inf'), complex('1-infj'), -0.0]),
+ (['F'], IEEE_754_FLOAT_COMPLEX_BE, '>FFFF',
+ [16711938.0j, float('inf'), complex('1-infj'), -0.0]),
+ (['D'], IEEE_754_DOUBLE_COMPLEX_LE, '<DDDD',
+ [9006104071832581.0j, float('inf'), complex('1-infj'), -0.0]),
+ (['D'], IEEE_754_DOUBLE_COMPLEX_BE, '>DDDD',
+ [9006104071832581.0j, float('inf'), complex('1-infj'), -0.0]),
)
for testcase in testcases:
valid_typecodes, mformat_code, struct_fmt, values = testcase
example = self.example
a = array.array(self.typecode, example)
self.assertRaises(TypeError, a.byteswap, 42)
- if a.itemsize in (1, 2, 4, 8):
+ if a.itemsize in (1, 2, 4, 8, 16):
b = array.array(self.typecode, example)
b.byteswap()
if a.itemsize==1:
b.byteswap()
self.assertEqual(a, b)
+class CFPTest(NumberTest):
+ example = [-42j, 0, 42+1j, 1e5j, -1e10]
+ outside = 23
+
+ def assertEntryEqual(self, entry1, entry2):
+ self.assertAlmostEqual(entry1, entry2)
+
+ def test_cmp(self):
+ a = array.array(self.typecode, self.example)
+ self.assertIs(a == 42, False)
+ self.assertIs(a != 42, True)
+
+ self.assertIs(a == a, True)
+ self.assertIs(a != a, False)
+ self.assertIs(a < a, False)
+ self.assertIs(a <= a, True)
+ self.assertIs(a > a, False)
+ self.assertIs(a >= a, True)
+
+ self.assertIs(a == 2*a, False)
+ self.assertIs(a != 2*a, True)
+ self.assertIs(a < 2*a, True)
+ self.assertIs(a <= 2*a, True)
+ self.assertIs(a > 2*a, False)
+ self.assertIs(a >= 2*a, False)
+
+ def test_nan(self):
+ a = array.array(self.typecode, [float('nan')])
+ b = array.array(self.typecode, [float('nan')])
+ self.assertIs(a != b, True)
+ self.assertIs(a == b, False)
+
+ def test_byteswap(self):
+ a = array.array(self.typecode, self.example)
+ self.assertRaises(TypeError, a.byteswap, 42)
+ if a.itemsize in (1, 2, 4, 8):
+ b = array.array(self.typecode, self.example)
+ b.byteswap()
+ if a.itemsize == 1:
+ self.assertEqual(a, b)
+ else:
+ # On alphas treating the byte swapped bit patterns as
+ # floats/doubles results in floating-point exceptions
+ # => compare the 8bit string values instead
+ self.assertNotEqual(a.tobytes(), b.tobytes())
+ b.byteswap()
+ self.assertEqual(a, b)
+
+
class FloatTest(FPTest, unittest.TestCase):
typecode = 'f'
minitemsize = 4
self.fail("Array of size > maxsize created - MemoryError expected")
+class ComplexFloatTest(CFPTest, unittest.TestCase):
+ typecode = 'F'
+ minitemsize = 8
+
+class ComplexDoubleTest(CFPTest, unittest.TestCase):
+ typecode = 'D'
+ minitemsize = 16
+
+
class LargeArrayTest(unittest.TestCase):
typecode = 'b'
--- /dev/null
+Support the :c:expr:`float complex` and :c:expr:`double complex` C types
+in the :mod:`array` module: formatting characters ``'F'`` and ``'D'``
+respectively. Patch by Sergey B Kirpichev.
UTF16_LE = 18,
UTF16_BE = 19,
UTF32_LE = 20,
- UTF32_BE = 21
+ UTF32_BE = 21,
+ IEEE_754_FLOAT_COMPLEX_LE = 22,
+ IEEE_754_FLOAT_COMPLEX_BE = 23,
+ IEEE_754_DOUBLE_COMPLEX_LE = 24,
+ IEEE_754_DOUBLE_COMPLEX_BE = 25
};
#define MACHINE_FORMAT_CODE_MIN 0
-#define MACHINE_FORMAT_CODE_MAX 21
+#define MACHINE_FORMAT_CODE_MAX 25
/*
return 0;
}
+static PyObject *
+cf_getitem(arrayobject *ap, Py_ssize_t i)
+{
+ float f[2];
+
+ memcpy(&f, ap->ob_item + i*sizeof(f), sizeof(f));
+ return PyComplex_FromDoubles(f[0], f[1]);
+}
+
+static int
+cf_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
+{
+ Py_complex x;
+ float f[2];
+
+ if (!PyArg_Parse(v, "D;array item must be complex", &x)) {
+ return -1;
+ }
+
+ CHECK_ARRAY_BOUNDS(ap, i);
+
+ f[0] = (float)x.real;
+ f[1] = (float)x.imag;
+ if (i >= 0) {
+ memcpy(ap->ob_item + i*sizeof(f), &f, sizeof(f));
+ }
+ return 0;
+}
+
+static PyObject *
+cd_getitem(arrayobject *ap, Py_ssize_t i)
+{
+ double f[2];
+
+ memcpy(&f, ap->ob_item + i*sizeof(f), sizeof(f));
+ return PyComplex_FromDoubles(f[0], f[1]);
+}
+
+static int
+cd_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
+{
+ Py_complex x;
+ double f[2];
+
+ if (!PyArg_Parse(v, "D;array item must be complex", &x)) {
+ return -1;
+ }
+
+ CHECK_ARRAY_BOUNDS(ap, i);
+
+ f[0] = x.real;
+ f[1] = x.imag;
+ if (i >= 0) {
+ memcpy(ap->ob_item + i*sizeof(f), &f, sizeof(f));
+ }
+ return 0;
+}
+
#define DEFINE_COMPAREITEMS(code, type) \
static int \
code##_compareitems(const void *lhs, const void *rhs, Py_ssize_t length) \
{'Q', sizeof(long long), QQ_getitem, QQ_setitem, QQ_compareitems, "Q", 1, 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},
+ {'D', 2*sizeof(double), cd_getitem, cd_setitem, NULL, "D", 0, 0},
{'\0', 0, 0, 0, 0, 0, 0} /* Sentinel */
};
Byteswap all items of the array.
-If the items in the array are not 1, 2, 4, or 8 bytes in size, RuntimeError is
-raised.
+If the items in the array are not 1, 2, 4, 8 or 16 bytes in size, RuntimeError
+is raised. Note, that for complex types the order of
+components (the real part, followed by imaginary part) is preserved.
[clinic start generated code]*/
static PyObject *
array_array_byteswap_impl(arrayobject *self)
-/*[clinic end generated code: output=5f8236cbdf0d90b5 input=9af1d1749000b14f]*/
+/*[clinic end generated code: output=5f8236cbdf0d90b5 input=aafda275f48191d0]*/
{
char *p;
Py_ssize_t i;
}
break;
case 8:
+ if (self->ob_descr->typecode != 'F') {
+ for (p = self->ob_item, i = Py_SIZE(self); --i >= 0; p += 8) {
+ char p0 = p[0];
+ char p1 = p[1];
+ char p2 = p[2];
+ char p3 = p[3];
+ p[0] = p[7];
+ p[1] = p[6];
+ p[2] = p[5];
+ p[3] = p[4];
+ p[4] = p3;
+ p[5] = p2;
+ p[6] = p1;
+ p[7] = p0;
+ }
+ }
+ else {
+ for (p = self->ob_item, i = Py_SIZE(self); --i >= 0; p += 8) {
+ char t0 = p[0];
+ char t1 = p[1];
+ p[0] = p[3];
+ p[1] = p[2];
+ p[2] = t1;
+ p[3] = t0;
+ t0 = p[4];
+ t1 = p[5];
+ p[4] = p[7];
+ p[5] = p[6];
+ p[6] = t1;
+ p[7] = t0;
+ }
+ }
+ break;
+ case 16:
+ assert(self->ob_descr->typecode == 'D');
for (p = self->ob_item, i = Py_SIZE(self); --i >= 0; p += 8) {
- char p0 = p[0];
- char p1 = p[1];
- char p2 = p[2];
- char p3 = p[3];
+ char t0 = p[0];
+ char t1 = p[1];
+ char t2 = p[2];
+ char t3 = p[3];
p[0] = p[7];
p[1] = p[6];
p[2] = p[5];
p[3] = p[4];
- p[4] = p3;
- p[5] = p2;
- p[6] = p1;
- p[7] = p0;
+ p[4] = t3;
+ p[5] = t2;
+ p[6] = t1;
+ p[7] = t0;
+ t0 = p[8];
+ t1 = p[9];
+ t2 = p[10];
+ t3 = p[11];
+ p[8] = p[15];
+ p[9] = p[14];
+ p[10] = p[13];
+ p[11] = p[12];
+ p[12] = t3;
+ p[13] = t2;
+ p[14] = t1;
+ p[15] = t0;
}
break;
default:
{4, 0, 0}, /* 18: UTF16_LE */
{4, 0, 1}, /* 19: UTF16_BE */
{8, 0, 0}, /* 20: UTF32_LE */
- {8, 0, 1} /* 21: UTF32_BE */
+ {8, 0, 1}, /* 21: UTF32_BE */
+ {8, 0, 0}, /* 22: IEEE_754_FLOAT_COMPLEX_LE */
+ {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 */
};
case 'd':
return _PY_FLOAT_BIG_ENDIAN ? IEEE_754_DOUBLE_BE : IEEE_754_DOUBLE_LE;
+ case 'F':
+ return _PY_FLOAT_BIG_ENDIAN ? \
+ IEEE_754_FLOAT_COMPLEX_BE : IEEE_754_FLOAT_COMPLEX_LE;
+
+ case 'D':
+ return _PY_FLOAT_BIG_ENDIAN ? \
+ IEEE_754_DOUBLE_COMPLEX_BE : IEEE_754_DOUBLE_COMPLEX_LE;
+
/* Integers */
case 'h':
intsize = sizeof(short);
}
break;
}
+ case IEEE_754_FLOAT_COMPLEX_LE:
+ case IEEE_754_FLOAT_COMPLEX_BE: {
+ Py_ssize_t i;
+ int le = (mformat_code == IEEE_754_FLOAT_COMPLEX_LE) ? 1 : 0;
+ Py_ssize_t itemcount = Py_SIZE(items) / 8;
+ 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 *pycomplex = PyComplex_FromDoubles(
+ PyFloat_Unpack4(&memstr[i * 8], le),
+ PyFloat_Unpack4(&memstr[i * 8] + 4, le));
+ if (pycomplex == NULL) {
+ Py_DECREF(converted_items);
+ return NULL;
+ }
+ PyList_SET_ITEM(converted_items, i, pycomplex);
+ }
+ break;
+ }
+ case IEEE_754_DOUBLE_COMPLEX_LE:
+ case IEEE_754_DOUBLE_COMPLEX_BE: {
+ Py_ssize_t i;
+ int le = (mformat_code == IEEE_754_DOUBLE_COMPLEX_LE) ? 1 : 0;
+ Py_ssize_t itemcount = Py_SIZE(items) / 16;
+ 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 *pycomplex = PyComplex_FromDoubles(
+ PyFloat_Unpack8(&memstr[i * 16], le),
+ PyFloat_Unpack8(&memstr[i * 16] + 8, le));
+ if (pycomplex == NULL) {
+ Py_DECREF(converted_items);
+ return NULL;
+ }
+ PyList_SET_ITEM(converted_items, i, pycomplex);
+ }
+ break;
+ }
case UTF16_LE:
case UTF16_BE: {
int byteorder = (mformat_code == UTF16_LE) ? -1 : 1;
PyDoc_STRVAR(module_doc,
"This module defines an object type which can efficiently represent\n\
an array of basic values: characters, integers, floating-point\n\
-numbers. Arrays are sequence types and behave very much like lists,\n\
+numbers, complex numbers. Arrays are sequence types and behave very much like lists,\n\
except that the type of objects stored in them is constrained.\n");
PyDoc_STRVAR(arraytype_doc,
'Q' unsigned integer 8 (see note)\n\
'f' floating-point 4\n\
'd' floating-point 8\n\
+ 'F' float complex 8\n\
+ 'D' double complex 16\n\
\n\
NOTE: The 'u' typecode corresponds to Python's unicode character. On\n\
narrow builds this is 2-bytes on wide builds this is 4-bytes.\n\
"\n"
"Byteswap all items of the array.\n"
"\n"
-"If the items in the array are not 1, 2, 4, or 8 bytes in size, RuntimeError is\n"
-"raised.");
+"If the items in the array are not 1, 2, 4, 8 or 16 bytes in size, RuntimeError\n"
+"is raised. Note, that for complex types the order of\n"
+"components (the real part, followed by imaginary part) is preserved.");
#define ARRAY_ARRAY_BYTESWAP_METHODDEF \
{"byteswap", (PyCFunction)array_array_byteswap, METH_NOARGS, array_array_byteswap__doc__},
return return_value;
}
-/*[clinic end generated code: output=c993c3598085840e input=a9049054013a1b77]*/
+/*[clinic end generated code: output=9dcb2fc40710f83d input=a9049054013a1b77]*/