Skip to content

Commit 66d7e54

Browse files
committed
gh-121249: make complex types available unconditionally in the struct module
Each complex type interpreted as an array type containing exactly two elements of the corresponding real type (real and imaginary parts, respectively).
1 parent bce45bc commit 66d7e54

File tree

5 files changed

+22
-89
lines changed

5 files changed

+22
-89
lines changed

Doc/library/struct.rst

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -260,24 +260,17 @@ platform-dependent.
260260
+--------+--------------------------+--------------------+----------------+------------+
261261
| ``d`` | :c:expr:`double` | float | 8 | \(4) |
262262
+--------+--------------------------+--------------------+----------------+------------+
263+
| ``F`` | :c:expr:`float complex` | complex | 8 | \(10) |
264+
+--------+--------------------------+--------------------+----------------+------------+
265+
| ``D`` | :c:expr:`double complex` | complex | 16 | \(10) |
266+
+--------+--------------------------+--------------------+----------------+------------+
263267
| ``s`` | :c:expr:`char[]` | bytes | | \(9) |
264268
+--------+--------------------------+--------------------+----------------+------------+
265269
| ``p`` | :c:expr:`char[]` | bytes | | \(8) |
266270
+--------+--------------------------+--------------------+----------------+------------+
267271
| ``P`` | :c:expr:`void \*` | integer | | \(5) |
268272
+--------+--------------------------+--------------------+----------------+------------+
269273

270-
Additionally, if IEC 60559 compatible complex arithmetic (Annex G of the
271-
C11 standard) is supported, the following format characters are available:
272-
273-
+--------+--------------------------+--------------------+----------------+------------+
274-
| Format | C Type | Python type | Standard size | Notes |
275-
+========+==========================+====================+================+============+
276-
| ``F`` | :c:expr:`float complex` | complex | 8 | \(10) |
277-
+--------+--------------------------+--------------------+----------------+------------+
278-
| ``D`` | :c:expr:`double complex` | complex | 16 | \(10) |
279-
+--------+--------------------------+--------------------+----------------+------------+
280-
281274
.. versionchanged:: 3.3
282275
Added support for the ``'n'`` and ``'N'`` formats.
283276

@@ -367,6 +360,12 @@ Notes:
367360
For the ``'E'`` and ``'C'`` format characters, the packed representation uses
368361
the IEEE 754 binary32 and binary64 format for components of the complex
369362
number, regardless of the floating-point format used by the platform.
363+
Note that support for complex types is available unconditionally,
364+
regardless on support IEC 60559 compatible complex arithmetic (Annex G of
365+
the C11 standard) by the compiler. Each complex type is interpreted as an
366+
array type containing exactly two elements of the corresponding real type
367+
(real and imaginary parts, respectively).
368+
370369

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

Doc/whatsnew/3.14.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1309,8 +1309,8 @@ struct
13091309
------
13101310

13111311
* Support the :c:expr:`float complex` and :c:expr:`double complex` C types in
1312-
the :mod:`struct` module (formatting characters ``'F'`` and ``'D'``,
1313-
respectively) if the compiler has C11 complex arithmetic.
1312+
the :mod:`struct` module (formatting characters ``'F'`` and ``'D'``
1313+
respectively).
13141314
(Contributed by Sergey B Kirpichev in :gh:`121249`.)
13151315

13161316

Lib/test/test_struct.py

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,6 @@
2222
INF = float('inf')
2323
NAN = float('nan')
2424

25-
try:
26-
struct.pack('D', 1j)
27-
have_c_complex = True
28-
except struct.error:
29-
have_c_complex = False
30-
3125
def iter_integer_formats(byteorders=byteorders):
3226
for code in integer_codes:
3327
for byteorder in byteorders:
@@ -796,7 +790,6 @@ def test_repr(self):
796790
s = struct.Struct('=i2H')
797791
self.assertEqual(repr(s), f'Struct({s.format!r})')
798792

799-
@unittest.skipUnless(have_c_complex, "requires C11 complex type support")
800793
def test_c_complex_round_trip(self):
801794
values = [complex(*_) for _ in combinations([1, -1, 0.0, -0.0, 2,
802795
-3, INF, -INF, NAN], 2)]
@@ -806,19 +799,6 @@ def test_c_complex_round_trip(self):
806799
round_trip = struct.unpack(f, struct.pack(f, z))[0]
807800
self.assertComplexesAreIdentical(z, round_trip)
808801

809-
@unittest.skipIf(have_c_complex, "requires no C11 complex type support")
810-
def test_c_complex_error(self):
811-
msg1 = "'F' format not supported on this system"
812-
msg2 = "'D' format not supported on this system"
813-
with self.assertRaisesRegex(struct.error, msg1):
814-
struct.pack('F', 1j)
815-
with self.assertRaisesRegex(struct.error, msg1):
816-
struct.unpack('F', b'1')
817-
with self.assertRaisesRegex(struct.error, msg2):
818-
struct.pack('D', 1j)
819-
with self.assertRaisesRegex(struct.error, msg2):
820-
struct.unpack('D', b'1')
821-
822802

823803
class UnpackIteratorTest(unittest.TestCase):
824804
"""
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Support the :c:expr:`float complex` and :c:expr:`double complex` C types in
2+
the :mod:`struct` module. Patch by Sergey B Kirpichev.

Modules/_struct.c

Lines changed: 8 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@
1212
#include "pycore_long.h" // _PyLong_AsByteArray()
1313
#include "pycore_moduleobject.h" // _PyModule_GetState()
1414

15-
#ifdef Py_HAVE_C_COMPLEX
16-
# include "_complex.h" // complex
17-
#endif
1815
#include <stddef.h> // offsetof()
1916

2017
/*[clinic input]
@@ -495,25 +492,23 @@ nu_double(_structmodulestate *state, const char *p, const formatdef *f)
495492
return PyFloat_FromDouble(x);
496493
}
497494

498-
#ifdef Py_HAVE_C_COMPLEX
499495
static PyObject *
500496
nu_float_complex(_structmodulestate *state, const char *p, const formatdef *f)
501497
{
502-
float complex x;
498+
float x[2];
503499

504500
memcpy(&x, p, sizeof(x));
505-
return PyComplex_FromDoubles(creal(x), cimag(x));
501+
return PyComplex_FromDoubles(x[0], x[1]);
506502
}
507503

508504
static PyObject *
509505
nu_double_complex(_structmodulestate *state, const char *p, const formatdef *f)
510506
{
511-
double complex x;
507+
double x[2];
512508

513509
memcpy(&x, p, sizeof(x));
514-
return PyComplex_FromDoubles(creal(x), cimag(x));
510+
return PyComplex_FromDoubles(x[0], x[1]);
515511
}
516-
#endif
517512

518513
static PyObject *
519514
nu_void_p(_structmodulestate *state, const char *p, const formatdef *f)
@@ -788,13 +783,12 @@ np_double(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
788783
return 0;
789784
}
790785

791-
#ifdef Py_HAVE_C_COMPLEX
792786
static int
793787
np_float_complex(_structmodulestate *state, char *p, PyObject *v,
794788
const formatdef *f)
795789
{
796790
Py_complex c = PyComplex_AsCComplex(v);
797-
float complex x = CMPLXF((float)c.real, (float)c.imag);
791+
float x[2] = {(float)c.real, (float)c.imag};
798792

799793
if (c.real == -1 && PyErr_Occurred()) {
800794
PyErr_SetString(state->StructError,
@@ -810,7 +804,7 @@ np_double_complex(_structmodulestate *state, char *p, PyObject *v,
810804
const formatdef *f)
811805
{
812806
Py_complex c = PyComplex_AsCComplex(v);
813-
double complex x = CMPLX(c.real, c.imag);
807+
double x[2] = {c.real, c.imag};
814808

815809
if (c.real == -1 && PyErr_Occurred()) {
816810
PyErr_SetString(state->StructError,
@@ -820,25 +814,6 @@ np_double_complex(_structmodulestate *state, char *p, PyObject *v,
820814
memcpy(p, &x, sizeof(x));
821815
return 0;
822816
}
823-
#else
824-
static int
825-
np_complex_stub(_structmodulestate *state, char *p, PyObject *v,
826-
const formatdef *f)
827-
{
828-
PyErr_Format(state->StructError,
829-
"'%c' format not supported on this system",
830-
f->format);
831-
return -1;
832-
}
833-
static PyObject *
834-
nu_complex_stub(_structmodulestate *state, const char *p, const formatdef *f)
835-
{
836-
PyErr_Format(state->StructError,
837-
"'%c' format not supported on this system",
838-
f->format);
839-
return NULL;
840-
}
841-
#endif
842817

843818
static int
844819
np_void_p(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
@@ -878,13 +853,8 @@ static const formatdef native_table[] = {
878853
{'e', sizeof(short), _Alignof(short), nu_halffloat, np_halffloat},
879854
{'f', sizeof(float), _Alignof(float), nu_float, np_float},
880855
{'d', sizeof(double), _Alignof(double), nu_double, np_double},
881-
#ifdef Py_HAVE_C_COMPLEX
882-
{'F', sizeof(float complex), _Alignof(float complex), nu_float_complex, np_float_complex},
883-
{'D', sizeof(double complex), _Alignof(double complex), nu_double_complex, np_double_complex},
884-
#else
885-
{'F', 1, 0, nu_complex_stub, np_complex_stub},
886-
{'D', 1, 0, nu_complex_stub, np_complex_stub},
887-
#endif
856+
{'F', 2*sizeof(float), _Alignof(float[2]), nu_float_complex, np_float_complex},
857+
{'D', 2*sizeof(double), _Alignof(double[2]), nu_double_complex, np_double_complex},
888858
{'P', sizeof(void *), _Alignof(void *), nu_void_p, np_void_p},
889859
{0}
890860
};
@@ -985,7 +955,6 @@ bu_double(_structmodulestate *state, const char *p, const formatdef *f)
985955
return unpack_double(p, 0);
986956
}
987957

988-
#ifdef Py_HAVE_C_COMPLEX
989958
static PyObject *
990959
bu_float_complex(_structmodulestate *state, const char *p, const formatdef *f)
991960
{
@@ -1015,7 +984,6 @@ bu_double_complex(_structmodulestate *state, const char *p, const formatdef *f)
1015984
}
1016985
return PyComplex_FromDoubles(x, y);
1017986
}
1018-
#endif
1019987

1020988
static PyObject *
1021989
bu_bool(_structmodulestate *state, const char *p, const formatdef *f)
@@ -1156,7 +1124,6 @@ bp_double(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
11561124
return PyFloat_Pack8(x, p, 0);
11571125
}
11581126

1159-
#ifdef Py_HAVE_C_COMPLEX
11601127
static int
11611128
bp_float_complex(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
11621129
{
@@ -1186,7 +1153,6 @@ bp_double_complex(_structmodulestate *state, char *p, PyObject *v, const formatd
11861153
}
11871154
return PyFloat_Pack8(x.imag, p + 8, 0);
11881155
}
1189-
#endif
11901156

11911157
static int
11921158
bp_bool(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
@@ -1218,13 +1184,8 @@ static formatdef bigendian_table[] = {
12181184
{'e', 2, 0, bu_halffloat, bp_halffloat},
12191185
{'f', 4, 0, bu_float, bp_float},
12201186
{'d', 8, 0, bu_double, bp_double},
1221-
#ifdef Py_HAVE_C_COMPLEX
12221187
{'F', 8, 0, bu_float_complex, bp_float_complex},
12231188
{'D', 16, 0, bu_double_complex, bp_double_complex},
1224-
#else
1225-
{'F', 1, 0, nu_complex_stub, np_complex_stub},
1226-
{'D', 1, 0, nu_complex_stub, np_complex_stub},
1227-
#endif
12281189
{0}
12291190
};
12301191

@@ -1324,7 +1285,6 @@ lu_double(_structmodulestate *state, const char *p, const formatdef *f)
13241285
return unpack_double(p, 1);
13251286
}
13261287

1327-
#ifdef Py_HAVE_C_COMPLEX
13281288
static PyObject *
13291289
lu_float_complex(_structmodulestate *state, const char *p, const formatdef *f)
13301290
{
@@ -1354,7 +1314,6 @@ lu_double_complex(_structmodulestate *state, const char *p, const formatdef *f)
13541314
}
13551315
return PyComplex_FromDoubles(x, y);
13561316
}
1357-
#endif
13581317

13591318
static int
13601319
lp_int(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
@@ -1489,7 +1448,6 @@ lp_double(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
14891448
return PyFloat_Pack8(x, p, 1);
14901449
}
14911450

1492-
#ifdef Py_HAVE_C_COMPLEX
14931451
static int
14941452
lp_float_complex(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
14951453
{
@@ -1520,7 +1478,6 @@ lp_double_complex(_structmodulestate *state, char *p, PyObject *v, const formatd
15201478
}
15211479
return PyFloat_Pack8(x.imag, p + 8, 1);
15221480
}
1523-
#endif
15241481

15251482
static formatdef lilendian_table[] = {
15261483
{'x', 1, 0, NULL},
@@ -1542,13 +1499,8 @@ static formatdef lilendian_table[] = {
15421499
{'e', 2, 0, lu_halffloat, lp_halffloat},
15431500
{'f', 4, 0, lu_float, lp_float},
15441501
{'d', 8, 0, lu_double, lp_double},
1545-
#ifdef Py_HAVE_C_COMPLEX
15461502
{'F', 8, 0, lu_float_complex, lp_float_complex},
15471503
{'D', 16, 0, lu_double_complex, lp_double_complex},
1548-
#else
1549-
{'F', 1, 0, nu_complex_stub, np_complex_stub},
1550-
{'D', 1, 0, nu_complex_stub, np_complex_stub},
1551-
#endif
15521504
{0}
15531505
};
15541506

0 commit comments

Comments
 (0)