Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions Doc/library/struct.rst
Original file line number Diff line number Diff line change
Expand Up @@ -267,12 +267,26 @@ platform-dependent.
| ``P`` | :c:expr:`void \*` | integer | | \(5) |
+--------+--------------------------+--------------------+----------------+------------+

Additionally, if IEC 60559 compatible complex arithmetic (Annex G of the
C11 standard) is supported, the following format characters are available:

+--------+--------------------------+--------------------+----------------+------------+
| Format | C Type | Python type | Standard size | Notes |
+========+==========================+====================+================+============+
| ``E`` | :c:expr:`float complex` | complex | 8 | \(10) |
+--------+--------------------------+--------------------+----------------+------------+
| ``C`` | :c:expr:`double complex` | complex | 16 | \(10) |
+--------+--------------------------+--------------------+----------------+------------+

.. versionchanged:: 3.3
Added support for the ``'n'`` and ``'N'`` formats.

.. versionchanged:: 3.6
Added support for the ``'e'`` format.

.. versionchanged:: 3.14
Added support for the ``'E'`` and ``'C'`` formats.


Notes:

Expand Down Expand Up @@ -349,6 +363,11 @@ Notes:
of bytes. As a special case, ``'0s'`` means a single, empty string (while
``'0c'`` means 0 characters).

(10)
For the ``'E'`` and ``'C'`` format characters, the packed representation uses
the IEEE 754 binary32 and binary64 format for components of the complex
number, regardless of the floating-point format used by the platform.

A format character may be preceded by an integral repeat count. For example,
the format string ``'4h'`` means exactly the same as ``'hhhh'``.

Expand Down
2 changes: 2 additions & 0 deletions Lib/ctypes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,10 @@ class c_longdouble(_SimpleCData):
try:
class c_double_complex(_SimpleCData):
_type_ = "C"
_check_size(c_double_complex)
class c_float_complex(_SimpleCData):
_type_ = "E"
_check_size(c_float_complex)
class c_longdouble_complex(_SimpleCData):
_type_ = "F"
except AttributeError:
Expand Down
62 changes: 62 additions & 0 deletions Lib/test/test_struct.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from collections import abc
from itertools import combinations
import array
import gc
import math
Expand All @@ -17,6 +18,15 @@
integer_codes = 'b', 'B', 'h', 'H', 'i', 'I', 'l', 'L', 'q', 'Q', 'n', 'N'
byteorders = '', '@', '=', '<', '>', '!'

INF = float('inf')
NAN = float('nan')

try:
struct.pack('C', 1j)
have_c_complex = True
except struct.error:
have_c_complex = False

def iter_integer_formats(byteorders=byteorders):
for code in integer_codes:
for byteorder in byteorders:
Expand All @@ -34,6 +44,34 @@ def bigendian_to_native(value):
return string_reverse(value)

class StructTest(unittest.TestCase):
# from Lib/test/test_complex.py
def assertFloatsAreIdentical(self, x, y):
"""assert that floats x and y are identical, in the sense that:
(1) both x and y are nans, or
(2) both x and y are infinities, with the same sign, or
(3) both x and y are zeros, with the same sign, or
(4) x and y are both finite and nonzero, and x == y

"""
msg = 'floats {!r} and {!r} are not identical'

if math.isnan(x) or math.isnan(y):
if math.isnan(x) and math.isnan(y):
return
elif x == y:
if x != 0.0:
return
# both zero; check that signs match
elif math.copysign(1.0, x) == math.copysign(1.0, y):
return
else:
msg += ': zeros have different signs'
self.fail(msg.format(x, y))

def assertComplexesAreIdentical(self, x, y):
self.assertFloatsAreIdentical(x.real, y.real)
self.assertFloatsAreIdentical(x.imag, y.imag)

def test_isbigendian(self):
self.assertEqual((struct.pack('=i', 1)[0] == 0), ISBIGENDIAN)

Expand Down Expand Up @@ -774,6 +812,30 @@ def test_repr(self):
s = struct.Struct('=i2H')
self.assertEqual(repr(s), f'Struct({s.format!r})')

@unittest.skipUnless(have_c_complex, "requires C11 complex type")
def test_c_complex_round_trip(self):
values = [complex(*_) for _ in combinations([1, -1, 0.0, -0.0, 2,
-3, INF, -INF, NAN], 2)]
for z in values:
for f in ['E', 'C', '>E', '>C', '<E', '<C']:
with self.subTest(z=z, format=f):
round_trip = struct.unpack(f, struct.pack(f, z))[0]
self.assertComplexesAreIdentical(z, round_trip)

@unittest.skipIf(have_c_complex, "requires no C11 complex type")
def test_c_complex_error(self):
msg1 = "'E' format not supported on this system"
msg2 = "'C' format not supported on this system"
with self.assertRaisesRegex(struct.error, msg1):
struct.pack('E', 1j)
with self.assertRaisesRegex(struct.error, msg1):
struct.unpack('E', b'1')
with self.assertRaisesRegex(struct.error, msg2):
struct.pack('C', 1j)
with self.assertRaisesRegex(struct.error, msg2):
struct.unpack('C', b'1')


class UnpackIteratorTest(unittest.TestCase):
"""
Tests for iterative unpacking (struct.Struct.iter_unpack).
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Support the :c:expr:`float complex` and :c:expr:`double complex`
C types in the :mod:`struct` module if the compiler has C11 complex
arithmetic. Patch by Sergey B Kirpichev.
Loading