]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-121249: Support _Complex types in the struct module (#121613)
authorSergey B Kirpichev <skirpichev@gmail.com>
Mon, 7 Oct 2024 11:53:02 +0000 (14:53 +0300)
committerGitHub <noreply@github.com>
Mon, 7 Oct 2024 11:53:02 +0000 (13:53 +0200)
Co-authored-by: Peter Bierma <zintensitydev@gmail.com>
Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
Co-authored-by: Victor Stinner <vstinner@python.org>
Doc/library/struct.rst
Lib/ctypes/__init__.py
Lib/test/test_struct.py
Misc/NEWS.d/next/Library/2024-07-10-08-13-34.gh-issue-121249.W9Gd09.rst [new file with mode: 0644]
Modules/_struct.c

index 4769affdf1d666217c05982e5125648d5150729f..3ea9e5ba07128986a79e41def281d48e1fa91d64 100644 (file)
@@ -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:
 
@@ -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'``.
 
index cb3a61287bfe5d21d99fdbfbeab2cd28e809514a..4a368f02d851c599c75d829913ffa9d95d6b76ed 100644 (file)
@@ -208,8 +208,10 @@ if sizeof(c_longdouble) == sizeof(c_double):
 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:
index bdbf8800cfd8f6d3f201515bd3628743b7298063..e3193c7863fbaef11ade54acc15f2edeaaa92067 100644 (file)
@@ -1,4 +1,5 @@
 from collections import abc
+from itertools import combinations
 import array
 import gc
 import math
@@ -11,12 +12,22 @@ import weakref
 from test import support
 from test.support import import_helper, suppress_immortalization
 from test.support.script_helper import assert_python_ok
+from test.support.testcase import ComplexesAreIdenticalMixin
 
 ISBIGENDIAN = sys.byteorder == "big"
 
 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:
@@ -33,7 +44,7 @@ def bigendian_to_native(value):
     else:
         return string_reverse(value)
 
-class StructTest(unittest.TestCase):
+class StructTest(ComplexesAreIdenticalMixin, unittest.TestCase):
     def test_isbigendian(self):
         self.assertEqual((struct.pack('=i', 1)[0] == 0), ISBIGENDIAN)
 
@@ -783,6 +794,30 @@ class StructTest(unittest.TestCase):
         s = struct.Struct('=i2H')
         self.assertEqual(repr(s), f'Struct({s.format!r})')
 
+    @unittest.skipUnless(have_c_complex, "requires C11 complex type support")
+    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 support")
+    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).
diff --git a/Misc/NEWS.d/next/Library/2024-07-10-08-13-34.gh-issue-121249.W9Gd09.rst b/Misc/NEWS.d/next/Library/2024-07-10-08-13-34.gh-issue-121249.W9Gd09.rst
new file mode 100644 (file)
index 0000000..2d41fca
--- /dev/null
@@ -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.
index 2ae5060ba34163b8329fb10370a98bee5a17db37..4387c55b7c88486efc810bb03e3c40283e513214 100644 (file)
@@ -12,6 +12,9 @@
 #include "pycore_long.h"          // _PyLong_AsByteArray()
 #include "pycore_moduleobject.h"  // _PyModule_GetState()
 
+#ifdef Py_HAVE_C_COMPLEX
+#  include "_complex.h"           // complex
+#endif
 #include <stddef.h>               // offsetof()
 
 /*[clinic input]
@@ -80,6 +83,10 @@ typedef struct { char c; int x; } st_int;
 typedef struct { char c; long x; } st_long;
 typedef struct { char c; float x; } st_float;
 typedef struct { char c; double x; } st_double;
+#ifdef Py_HAVE_C_COMPLEX
+typedef struct { char c; float complex x; } st_float_complex;
+typedef struct { char c; double complex x; } st_double_complex;
+#endif
 typedef struct { char c; void *x; } st_void_p;
 typedef struct { char c; size_t x; } st_size_t;
 typedef struct { char c; _Bool x; } st_bool;
@@ -89,6 +96,10 @@ typedef struct { char c; _Bool x; } st_bool;
 #define LONG_ALIGN (sizeof(st_long) - sizeof(long))
 #define FLOAT_ALIGN (sizeof(st_float) - sizeof(float))
 #define DOUBLE_ALIGN (sizeof(st_double) - sizeof(double))
+#ifdef Py_HAVE_C_COMPLEX
+#  define FLOAT_COMPLEX_ALIGN (sizeof(st_float_complex) - sizeof(float complex))
+#  define DOUBLE_COMPLEX_ALIGN (sizeof(st_double_complex) - sizeof(double complex))
+#endif
 #define VOID_P_ALIGN (sizeof(st_void_p) - sizeof(void *))
 #define SIZE_T_ALIGN (sizeof(st_size_t) - sizeof(size_t))
 #define BOOL_ALIGN (sizeof(st_bool) - sizeof(_Bool))
@@ -407,7 +418,7 @@ static PyObject *
 nu_short(_structmodulestate *state, const char *p, const formatdef *f)
 {
     short x;
-    memcpy((char *)&x, p, sizeof x);
+    memcpy(&x, p, sizeof x);
     return PyLong_FromLong((long)x);
 }
 
@@ -415,7 +426,7 @@ static PyObject *
 nu_ushort(_structmodulestate *state, const char *p, const formatdef *f)
 {
     unsigned short x;
-    memcpy((char *)&x, p, sizeof x);
+    memcpy(&x, p, sizeof x);
     return PyLong_FromLong((long)x);
 }
 
@@ -423,7 +434,7 @@ static PyObject *
 nu_int(_structmodulestate *state, const char *p, const formatdef *f)
 {
     int x;
-    memcpy((char *)&x, p, sizeof x);
+    memcpy(&x, p, sizeof x);
     return PyLong_FromLong((long)x);
 }
 
@@ -431,7 +442,7 @@ static PyObject *
 nu_uint(_structmodulestate *state, const char *p, const formatdef *f)
 {
     unsigned int x;
-    memcpy((char *)&x, p, sizeof x);
+    memcpy(&x, p, sizeof x);
     return PyLong_FromUnsignedLong((unsigned long)x);
 }
 
@@ -439,7 +450,7 @@ static PyObject *
 nu_long(_structmodulestate *state, const char *p, const formatdef *f)
 {
     long x;
-    memcpy((char *)&x, p, sizeof x);
+    memcpy(&x, p, sizeof x);
     return PyLong_FromLong(x);
 }
 
@@ -447,7 +458,7 @@ static PyObject *
 nu_ulong(_structmodulestate *state, const char *p, const formatdef *f)
 {
     unsigned long x;
-    memcpy((char *)&x, p, sizeof x);
+    memcpy(&x, p, sizeof x);
     return PyLong_FromUnsignedLong(x);
 }
 
@@ -455,7 +466,7 @@ static PyObject *
 nu_ssize_t(_structmodulestate *state, const char *p, const formatdef *f)
 {
     Py_ssize_t x;
-    memcpy((char *)&x, p, sizeof x);
+    memcpy(&x, p, sizeof x);
     return PyLong_FromSsize_t(x);
 }
 
@@ -463,7 +474,7 @@ static PyObject *
 nu_size_t(_structmodulestate *state, const char *p, const formatdef *f)
 {
     size_t x;
-    memcpy((char *)&x, p, sizeof x);
+    memcpy(&x, p, sizeof x);
     return PyLong_FromSize_t(x);
 }
 
@@ -471,7 +482,7 @@ static PyObject *
 nu_longlong(_structmodulestate *state, const char *p, const formatdef *f)
 {
     long long x;
-    memcpy((char *)&x, p, sizeof x);
+    memcpy(&x, p, sizeof x);
     return PyLong_FromLongLong(x);
 }
 
@@ -479,7 +490,7 @@ static PyObject *
 nu_ulonglong(_structmodulestate *state, const char *p, const formatdef *f)
 {
     unsigned long long x;
-    memcpy((char *)&x, p, sizeof x);
+    memcpy(&x, p, sizeof x);
     return PyLong_FromUnsignedLongLong(x);
 }
 
@@ -487,7 +498,7 @@ static PyObject *
 nu_bool(_structmodulestate *state, const char *p, const formatdef *f)
 {
     _Bool x;
-    memcpy((char *)&x, p, sizeof x);
+    memcpy(&x, p, sizeof x);
     return PyBool_FromLong(x != 0);
 }
 
@@ -506,7 +517,7 @@ static PyObject *
 nu_float(_structmodulestate *state, const char *p, const formatdef *f)
 {
     float x;
-    memcpy((char *)&x, p, sizeof x);
+    memcpy(&x, p, sizeof x);
     return PyFloat_FromDouble((double)x);
 }
 
@@ -514,15 +525,35 @@ static PyObject *
 nu_double(_structmodulestate *state, const char *p, const formatdef *f)
 {
     double x;
-    memcpy((char *)&x, p, sizeof x);
+    memcpy(&x, p, sizeof x);
     return PyFloat_FromDouble(x);
 }
 
+#ifdef Py_HAVE_C_COMPLEX
+static PyObject *
+nu_float_complex(_structmodulestate *state, const char *p, const formatdef *f)
+{
+    float complex x;
+
+    memcpy(&x, p, sizeof(x));
+    return PyComplex_FromDoubles(creal(x), cimag(x));
+}
+
+static PyObject *
+nu_double_complex(_structmodulestate *state, const char *p, const formatdef *f)
+{
+    double complex x;
+
+    memcpy(&x, p, sizeof(x));
+    return PyComplex_FromDoubles(creal(x), cimag(x));
+}
+#endif
+
 static PyObject *
 nu_void_p(_structmodulestate *state, const char *p, const formatdef *f)
 {
     void *x;
-    memcpy((char *)&x, p, sizeof x);
+    memcpy(&x, p, sizeof x);
     return PyLong_FromVoidPtr(x);
 }
 
@@ -587,7 +618,7 @@ np_short(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
         RANGE_ERROR(state, f, 0);
     }
     y = (short)x;
-    memcpy(p, (char *)&y, sizeof y);
+    memcpy(p, &y, sizeof y);
     return 0;
 }
 
@@ -606,7 +637,7 @@ np_ushort(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
         RANGE_ERROR(state, f, 1);
     }
     y = (unsigned short)x;
-    memcpy(p, (char *)&y, sizeof y);
+    memcpy(p, &y, sizeof y);
     return 0;
 }
 
@@ -626,7 +657,7 @@ np_int(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
         RANGE_ERROR(state, f, 0);
 #endif
     y = (int)x;
-    memcpy(p, (char *)&y, sizeof y);
+    memcpy(p, &y, sizeof y);
     return 0;
 }
 
@@ -646,7 +677,7 @@ np_uint(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
     if (x > ((unsigned long)UINT_MAX))
         RANGE_ERROR(state, f, 1);
 #endif
-    memcpy(p, (char *)&y, sizeof y);
+    memcpy(p, &y, sizeof y);
     return 0;
 }
 
@@ -660,7 +691,7 @@ np_long(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
         }
         return -1;
     }
-    memcpy(p, (char *)&x, sizeof x);
+    memcpy(p, &x, sizeof x);
     return 0;
 }
 
@@ -674,7 +705,7 @@ np_ulong(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
         }
         return -1;
     }
-    memcpy(p, (char *)&x, sizeof x);
+    memcpy(p, &x, sizeof x);
     return 0;
 }
 
@@ -688,7 +719,7 @@ np_ssize_t(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
         }
         return -1;
     }
-    memcpy(p, (char *)&x, sizeof x);
+    memcpy(p, &x, sizeof x);
     return 0;
 }
 
@@ -702,7 +733,7 @@ np_size_t(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
         }
         return -1;
     }
-    memcpy(p, (char *)&x, sizeof x);
+    memcpy(p, &x, sizeof x);
     return 0;
 }
 
@@ -720,7 +751,7 @@ np_longlong(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
         }
         return -1;
     }
-    memcpy(p, (char *)&x, sizeof x);
+    memcpy(p, &x, sizeof x);
     return 0;
 }
 
@@ -737,7 +768,7 @@ np_ulonglong(_structmodulestate *state, char *p, PyObject *v, const formatdef *f
         }
         return -1;
     }
-    memcpy(p, (char *)&x, sizeof x);
+    memcpy(p, &x, sizeof x);
     return 0;
 }
 
@@ -751,7 +782,7 @@ np_bool(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
     if (y < 0)
         return -1;
     x = y;
-    memcpy(p, (char *)&x, sizeof x);
+    memcpy(p, &x, sizeof x);
     return 0;
 }
 
@@ -774,7 +805,7 @@ np_float(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
                         "required argument is not a float");
         return -1;
     }
-    memcpy(p, (char *)&x, sizeof x);
+    memcpy(p, &x, sizeof x);
     return 0;
 }
 
@@ -787,9 +818,61 @@ np_double(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
                         "required argument is not a float");
         return -1;
     }
-    memcpy(p, (char *)&x, sizeof(double));
+    memcpy(p, &x, sizeof(double));
+    return 0;
+}
+
+#ifdef Py_HAVE_C_COMPLEX
+static int
+np_float_complex(_structmodulestate *state, char *p, PyObject *v,
+                 const formatdef *f)
+{
+    Py_complex c = PyComplex_AsCComplex(v);
+    float complex x = CMPLXF((float)c.real, (float)c.imag);
+
+    if (c.real == -1 && PyErr_Occurred()) {
+        PyErr_SetString(state->StructError,
+                        "required argument is not a complex");
+        return -1;
+    }
+    memcpy(p, &x, sizeof(x));
+    return 0;
+}
+
+static int
+np_double_complex(_structmodulestate *state, char *p, PyObject *v,
+                  const formatdef *f)
+{
+    Py_complex c = PyComplex_AsCComplex(v);
+    double complex x = CMPLX(c.real, c.imag);
+
+    if (c.real == -1 && PyErr_Occurred()) {
+        PyErr_SetString(state->StructError,
+                        "required argument is not a complex");
+        return -1;
+    }
+    memcpy(p, &x, sizeof(x));
     return 0;
 }
+#else
+static int
+np_complex_stub(_structmodulestate *state, char *p, PyObject *v,
+                const formatdef *f)
+{
+    PyErr_Format(state->StructError,
+                 "'%c' format not supported on this system",
+                 f->format);
+    return -1;
+}
+static PyObject *
+nu_complex_stub(_structmodulestate *state, const char *p, const formatdef *f)
+{
+    PyErr_Format(state->StructError,
+                 "'%c' format not supported on this system",
+                 f->format);
+    return NULL;
+}
+#endif
 
 static int
 np_void_p(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
@@ -804,7 +887,7 @@ np_void_p(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
     Py_DECREF(v);
     if (x == NULL && PyErr_Occurred())
         return -1;
-    memcpy(p, (char *)&x, sizeof x);
+    memcpy(p, &x, sizeof x);
     return 0;
 }
 
@@ -829,6 +912,13 @@ static const formatdef native_table[] = {
     {'e',       sizeof(short),  SHORT_ALIGN,    nu_halffloat,   np_halffloat},
     {'f',       sizeof(float),  FLOAT_ALIGN,    nu_float,       np_float},
     {'d',       sizeof(double), DOUBLE_ALIGN,   nu_double,      np_double},
+#ifdef Py_HAVE_C_COMPLEX
+    {'E',       sizeof(float complex), FLOAT_COMPLEX_ALIGN, nu_float_complex, np_float_complex},
+    {'C',       sizeof(double complex), DOUBLE_COMPLEX_ALIGN, nu_double_complex, np_double_complex},
+#else
+    {'E',       1, 0, nu_complex_stub, np_complex_stub},
+    {'C',       1, 0, nu_complex_stub, np_complex_stub},
+#endif
     {'P',       sizeof(void *), VOID_P_ALIGN,   nu_void_p,      np_void_p},
     {0}
 };
@@ -929,6 +1019,38 @@ bu_double(_structmodulestate *state, const char *p, const formatdef *f)
     return unpack_double(p, 0);
 }
 
+#ifdef Py_HAVE_C_COMPLEX
+static PyObject *
+bu_float_complex(_structmodulestate *state, const char *p, const formatdef *f)
+{
+    double x = PyFloat_Unpack4(p, 0);
+    if (x == -1.0 && PyErr_Occurred()) {
+        return NULL;
+    }
+    double y = PyFloat_Unpack4(p + 4, 0);
+    if (y == -1.0 && PyErr_Occurred()) {
+        return NULL;
+    }
+    return PyComplex_FromDoubles(x, y);
+}
+
+static PyObject *
+bu_double_complex(_structmodulestate *state, const char *p, const formatdef *f)
+{
+    double x, y;
+
+    x = PyFloat_Unpack8(p, 0);
+    if (x == -1.0 && PyErr_Occurred()) {
+        return NULL;
+    }
+    y = PyFloat_Unpack8(p + 8, 0);
+    if (y == -1.0 && PyErr_Occurred()) {
+        return NULL;
+    }
+    return PyComplex_FromDoubles(x, y);
+}
+#endif
+
 static PyObject *
 bu_bool(_structmodulestate *state, const char *p, const formatdef *f)
 {
@@ -1068,6 +1190,38 @@ bp_double(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
     return PyFloat_Pack8(x, p, 0);
 }
 
+#ifdef Py_HAVE_C_COMPLEX
+static int
+bp_float_complex(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
+{
+    Py_complex x = PyComplex_AsCComplex(v);
+    if (x.real == -1 && PyErr_Occurred()) {
+        PyErr_SetString(state->StructError,
+                        "required argument is not a complex");
+        return -1;
+    }
+    if (PyFloat_Pack4(x.real, p, 0)) {
+        return -1;
+    }
+    return PyFloat_Pack4(x.imag, p + 4, 0);
+}
+
+static int
+bp_double_complex(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
+{
+    Py_complex x = PyComplex_AsCComplex(v);
+    if (x.real == -1 && PyErr_Occurred()) {
+        PyErr_SetString(state->StructError,
+                        "required argument is not a complex");
+        return -1;
+    }
+    if (PyFloat_Pack8(x.real, p, 0)) {
+        return -1;
+    }
+    return PyFloat_Pack8(x.imag, p + 8, 0);
+}
+#endif
+
 static int
 bp_bool(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
 {
@@ -1098,6 +1252,13 @@ static formatdef bigendian_table[] = {
     {'e',       2,              0,              bu_halffloat,   bp_halffloat},
     {'f',       4,              0,              bu_float,       bp_float},
     {'d',       8,              0,              bu_double,      bp_double},
+#ifdef Py_HAVE_C_COMPLEX
+    {'E',       8,              0,              bu_float_complex, bp_float_complex},
+    {'C',       16,             0,              bu_double_complex, bp_double_complex},
+#else
+    {'E',       1,              0,              nu_complex_stub, np_complex_stub},
+    {'C',       1,              0,              nu_complex_stub, np_complex_stub},
+#endif
     {0}
 };
 
@@ -1197,6 +1358,38 @@ lu_double(_structmodulestate *state, const char *p, const formatdef *f)
     return unpack_double(p, 1);
 }
 
+#ifdef Py_HAVE_C_COMPLEX
+static PyObject *
+lu_float_complex(_structmodulestate *state, const char *p, const formatdef *f)
+{
+    double x = PyFloat_Unpack4(p, 1);
+    if (x == -1.0 && PyErr_Occurred()) {
+        return NULL;
+    }
+    double y = PyFloat_Unpack4(p + 4, 1);
+    if (y == -1.0 && PyErr_Occurred()) {
+        return NULL;
+    }
+    return PyComplex_FromDoubles(x, y);
+}
+
+static PyObject *
+lu_double_complex(_structmodulestate *state, const char *p, const formatdef *f)
+{
+    double x, y;
+
+    x = PyFloat_Unpack8(p, 1);
+    if (x == -1.0 && PyErr_Occurred()) {
+        return NULL;
+    }
+    y = PyFloat_Unpack8(p + 8, 1);
+    if (y == -1.0 && PyErr_Occurred()) {
+        return NULL;
+    }
+    return PyComplex_FromDoubles(x, y);
+}
+#endif
+
 static int
 lp_int(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
 {
@@ -1330,6 +1523,39 @@ lp_double(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
     return PyFloat_Pack8(x, p, 1);
 }
 
+#ifdef Py_HAVE_C_COMPLEX
+static int
+lp_float_complex(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
+{
+    Py_complex x = PyComplex_AsCComplex(v);
+    if (x.real == -1 && PyErr_Occurred()) {
+        PyErr_SetString(state->StructError,
+                        "required argument is not a complex");
+        return -1;
+    }
+    if (PyFloat_Pack4(x.real, p, 1)) {
+        return -1;
+    }
+    return PyFloat_Pack4(x.imag, p + 4, 1);
+
+}
+
+static int
+lp_double_complex(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
+{
+    Py_complex x = PyComplex_AsCComplex(v);
+    if (x.real == -1 && PyErr_Occurred()) {
+        PyErr_SetString(state->StructError,
+                        "required argument is not a complex");
+        return -1;
+    }
+    if (PyFloat_Pack8(x.real, p, 1)) {
+        return -1;
+    }
+    return PyFloat_Pack8(x.imag, p + 8, 1);
+}
+#endif
+
 static formatdef lilendian_table[] = {
     {'x',       1,              0,              NULL},
     {'b',       1,              0,              nu_byte,        np_byte},
@@ -1350,6 +1576,13 @@ static formatdef lilendian_table[] = {
     {'e',       2,              0,              lu_halffloat,   lp_halffloat},
     {'f',       4,              0,              lu_float,       lp_float},
     {'d',       8,              0,              lu_double,      lp_double},
+#ifdef Py_HAVE_C_COMPLEX
+    {'E',       8,              0,              lu_float_complex, lp_float_complex},
+    {'C',       16,             0,              lu_double_complex, lp_double_complex},
+#else
+    {'E',       1,              0,              nu_complex_stub, np_complex_stub},
+    {'C',       1,              0,              nu_complex_stub, np_complex_stub},
+#endif
     {0}
 };