Skip to content

Commit 0e3b3b8

Browse files
skirpichevvstinner
andauthored
gh-146151: Add support for complex arrays in the array module (#146237)
Co-authored-by: Victor Stinner <vstinner@python.org>
1 parent c68a194 commit 0e3b3b8

File tree

6 files changed

+296
-25
lines changed

6 files changed

+296
-25
lines changed

Doc/library/array.rst

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
--------------
1010

1111
This module defines an object type which can compactly represent an array of
12-
basic values: characters, integers, floating-point numbers. Arrays are mutable :term:`sequence`
12+
basic values: characters, integers, floating-point numbers, complex numbers. Arrays are mutable :term:`sequence`
1313
types and behave very much like lists, except that the type of objects stored in
1414
them is constrained. The type is specified at object creation time by using a
1515
:dfn:`type code`, which is a single character. The following type codes are
@@ -46,6 +46,11 @@ defined:
4646
+-----------+--------------------+-------------------+-----------------------+-------+
4747
| ``'d'`` | double | float | 8 | |
4848
+-----------+--------------------+-------------------+-----------------------+-------+
49+
| ``'F'`` | float complex | complex | 8 | \(3) |
50+
+-----------+--------------------+-------------------+-----------------------+-------+
51+
| ``'D'`` | double complex | complex | 16 | \(3) |
52+
+-----------+--------------------+-------------------+-----------------------+-------+
53+
4954

5055
Notes:
5156

@@ -63,6 +68,15 @@ Notes:
6368
(2)
6469
.. versionadded:: 3.13
6570

71+
(3)
72+
Complex types (``F`` and ``D``) are available unconditionally,
73+
regardless on support for complex types (the Annex G of the C11 standard)
74+
by the C compiler.
75+
As specified in the C11 standard, each complex type is represented by a
76+
two-element C array containing, respectively, the real and imaginary parts.
77+
78+
.. versionadded:: 3.15
79+
6680
.. seealso::
6781

6882
The :ref:`ctypes <ctypes-fundamental-data-types>` and
@@ -146,9 +160,10 @@ The module defines the following type:
146160
.. method:: byteswap()
147161

148162
"Byteswap" all items of the array. This is only supported for values which are
149-
1, 2, 4, or 8 bytes in size; for other types of values, :exc:`RuntimeError` is
163+
1, 2, 4, 8 or 16 bytes in size; for other types of values, :exc:`RuntimeError` is
150164
raised. It is useful when reading data from a file written on a machine with a
151-
different byte order.
165+
different byte order. Note, that for complex types the order of
166+
components (the real part, followed by imaginary part) is preserved.
152167

153168

154169
.. method:: count(x)

Doc/whatsnew/3.15.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,14 @@ argparse
634634
(Contributed by Savannah Ostrowski in :gh:`142390`.)
635635

636636

637+
array
638+
-----
639+
640+
* Support the :c:expr:`float complex` and :c:expr:`double complex` C types:
641+
formatting characters ``'F'`` and ``'D'`` respectively.
642+
(Contributed by Sergey B Kirpichev in :gh:`146151`.)
643+
644+
637645
base64
638646
------
639647

Lib/test/test_array.py

Lines changed: 76 additions & 4 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 = 'uwbBhHiIlLfdqQ'
34+
typecodes = 'uwbBhHiIlLfdqQFD'
3535

3636
class MiscTest(unittest.TestCase):
3737

@@ -113,6 +113,12 @@ def __index__(self):
113113
UTF16_BE = 19
114114
UTF32_LE = 20
115115
UTF32_BE = 21
116+
IEEE_754_FLOAT_COMPLEX_LE = 22
117+
IEEE_754_FLOAT_COMPLEX_BE = 23
118+
IEEE_754_DOUBLE_COMPLEX_LE = 24
119+
IEEE_754_DOUBLE_COMPLEX_BE = 25
120+
121+
MACHINE_FORMAT_CODE_MAX = 25
116122

117123

118124
class ArrayReconstructorTest(unittest.TestCase):
@@ -139,7 +145,7 @@ def test_error(self):
139145
self.assertRaises(ValueError, array_reconstructor,
140146
array.array, "b", UNKNOWN_FORMAT, b"")
141147
self.assertRaises(ValueError, array_reconstructor,
142-
array.array, "b", 22, b"")
148+
array.array, "b", MACHINE_FORMAT_CODE_MAX + 1, b"")
143149
self.assertRaises(ValueError, array_reconstructor,
144150
array.array, "d", 16, b"a")
145151

@@ -191,7 +197,15 @@ def test_numbers(self):
191197
(['d'], IEEE_754_DOUBLE_LE, '<dddd',
192198
[9006104071832581.0, float('inf'), float('-inf'), -0.0]),
193199
(['d'], IEEE_754_DOUBLE_BE, '>dddd',
194-
[9006104071832581.0, float('inf'), float('-inf'), -0.0])
200+
[9006104071832581.0, float('inf'), float('-inf'), -0.0]),
201+
(['F'], IEEE_754_FLOAT_COMPLEX_LE, '<FFFF',
202+
[16711938.0j, float('inf'), complex('1-infj'), -0.0]),
203+
(['F'], IEEE_754_FLOAT_COMPLEX_BE, '>FFFF',
204+
[16711938.0j, float('inf'), complex('1-infj'), -0.0]),
205+
(['D'], IEEE_754_DOUBLE_COMPLEX_LE, '<DDDD',
206+
[9006104071832581.0j, float('inf'), complex('1-infj'), -0.0]),
207+
(['D'], IEEE_754_DOUBLE_COMPLEX_BE, '>DDDD',
208+
[9006104071832581.0j, float('inf'), complex('1-infj'), -0.0]),
195209
)
196210
for testcase in testcases:
197211
valid_typecodes, mformat_code, struct_fmt, values = testcase
@@ -279,7 +293,7 @@ def test_byteswap(self):
279293
example = self.example
280294
a = array.array(self.typecode, example)
281295
self.assertRaises(TypeError, a.byteswap, 42)
282-
if a.itemsize in (1, 2, 4, 8):
296+
if a.itemsize in (1, 2, 4, 8, 16):
283297
b = array.array(self.typecode, example)
284298
b.byteswap()
285299
if a.itemsize==1:
@@ -1525,6 +1539,55 @@ def test_byteswap(self):
15251539
b.byteswap()
15261540
self.assertEqual(a, b)
15271541

1542+
class CFPTest(NumberTest):
1543+
example = [-42j, 0, 42+1j, 1e5j, -1e10]
1544+
outside = 23
1545+
1546+
def assertEntryEqual(self, entry1, entry2):
1547+
self.assertAlmostEqual(entry1, entry2)
1548+
1549+
def test_cmp(self):
1550+
a = array.array(self.typecode, self.example)
1551+
self.assertIs(a == 42, False)
1552+
self.assertIs(a != 42, True)
1553+
1554+
self.assertIs(a == a, True)
1555+
self.assertIs(a != a, False)
1556+
self.assertIs(a < a, False)
1557+
self.assertIs(a <= a, True)
1558+
self.assertIs(a > a, False)
1559+
self.assertIs(a >= a, True)
1560+
1561+
self.assertIs(a == 2*a, False)
1562+
self.assertIs(a != 2*a, True)
1563+
self.assertIs(a < 2*a, True)
1564+
self.assertIs(a <= 2*a, True)
1565+
self.assertIs(a > 2*a, False)
1566+
self.assertIs(a >= 2*a, False)
1567+
1568+
def test_nan(self):
1569+
a = array.array(self.typecode, [float('nan')])
1570+
b = array.array(self.typecode, [float('nan')])
1571+
self.assertIs(a != b, True)
1572+
self.assertIs(a == b, False)
1573+
1574+
def test_byteswap(self):
1575+
a = array.array(self.typecode, self.example)
1576+
self.assertRaises(TypeError, a.byteswap, 42)
1577+
if a.itemsize in (1, 2, 4, 8):
1578+
b = array.array(self.typecode, self.example)
1579+
b.byteswap()
1580+
if a.itemsize == 1:
1581+
self.assertEqual(a, b)
1582+
else:
1583+
# On alphas treating the byte swapped bit patterns as
1584+
# floats/doubles results in floating-point exceptions
1585+
# => compare the 8bit string values instead
1586+
self.assertNotEqual(a.tobytes(), b.tobytes())
1587+
b.byteswap()
1588+
self.assertEqual(a, b)
1589+
1590+
15281591
class FloatTest(FPTest, unittest.TestCase):
15291592
typecode = 'f'
15301593
minitemsize = 4
@@ -1551,6 +1614,15 @@ def test_alloc_overflow(self):
15511614
self.fail("Array of size > maxsize created - MemoryError expected")
15521615

15531616

1617+
class ComplexFloatTest(CFPTest, unittest.TestCase):
1618+
typecode = 'F'
1619+
minitemsize = 8
1620+
1621+
class ComplexDoubleTest(CFPTest, unittest.TestCase):
1622+
typecode = 'D'
1623+
minitemsize = 16
1624+
1625+
15541626
class LargeArrayTest(unittest.TestCase):
15551627
typecode = 'b'
15561628

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Support the :c:expr:`float complex` and :c:expr:`double complex` C types
2+
in the :mod:`array` module: formatting characters ``'F'`` and ``'D'``
3+
respectively. Patch by Sergey B Kirpichev.

0 commit comments

Comments
 (0)