Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for C99 complex type (_Complex) to the struct module #121249

Closed
skirpichev opened this issue Jul 2, 2024 · 7 comments
Closed

Add support for C99 complex type (_Complex) to the struct module #121249

skirpichev opened this issue Jul 2, 2024 · 7 comments
Labels
type-feature A feature request or enhancement

Comments

@skirpichev
Copy link
Member

skirpichev commented Jul 2, 2024

Feature or enhancement

Proposal:

The struct module has support for float and double types, so at least there should be also float _Complex and double _Complex. I'll work on a patch.

Initial version
diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py
index d2e6a8bfc8..d941036719 100644
--- a/Lib/ctypes/__init__.py
+++ b/Lib/ctypes/__init__.py
@@ -208,6 +208,7 @@ class c_longdouble(_SimpleCData):
 try:
     class c_double_complex(_SimpleCData):
         _type_ = "C"
+    _check_size(c_double_complex)
 except AttributeError:
     pass
 
diff --git a/Modules/_struct.c b/Modules/_struct.c
index 6a68478dd4..caf4975413 100644
--- a/Modules/_struct.c
+++ b/Modules/_struct.c
@@ -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,9 @@ 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; 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 +95,9 @@ 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 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))
@@ -518,6 +527,17 @@ nu_double(_structmodulestate *state, const char *p, const formatdef *f)
     return PyFloat_FromDouble(x);
 }
 
+#ifdef Py_HAVE_C_COMPLEX
+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)
 {
@@ -791,6 +811,24 @@ np_double(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
     return 0;
 }
 
+#ifdef Py_HAVE_C_COMPLEX
+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, (char *)&x, sizeof(x));
+    return 0;
+}
+#endif
+
 static int
 np_void_p(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
 {
@@ -829,6 +867,9 @@ 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
+    {'C',       sizeof(double complex), DOUBLE_COMPLEX_ALIGN, nu_double_complex, np_double_complex},
+#endif
     {'P',       sizeof(void *), VOID_P_ALIGN,   nu_void_p,      np_void_p},
     {0}
 };

Has this already been discussed elsewhere?

This is a minor feature, which does not need previous discussion elsewhere

Links to previous discussion of this feature:

No response

Linked PRs

@skirpichev skirpichev added the type-feature A feature request or enhancement label Jul 2, 2024
skirpichev added a commit to skirpichev/cpython that referenced this issue Jul 11, 2024
skirpichev added a commit to skirpichev/cpython that referenced this issue Jul 11, 2024
skirpichev added a commit to skirpichev/cpython that referenced this issue Jul 11, 2024
vstinner added a commit that referenced this issue Oct 7, 2024
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>
@vstinner
Copy link
Member

vstinner commented Oct 7, 2024

Implemented by change 7487db4.

@vstinner vstinner closed this as completed Oct 7, 2024
@efimov-mikhail
Copy link
Contributor

IMO, we should provide a support for long double complex in struct module.
For now there is a little inconvience:

-> % cat struct_calcsize_complex.py
import struct
print(struct.calcsize("d"))
print(struct.calcsize("C"))
print(struct.calcsize("E"))
print(struct.calcsize("F"))
-> % ./python struct_calcsize_complex.py
8
16
8
Traceback (most recent call last):
  File "/home/mikhail.efimov/projects/cpython/struct_calcsize_complex.py", line 5, in <module>
    print(struct.calcsize("F"))
          ~~~~~~~~~~~~~~~^^^^^
struct.error: bad char in struct format

@skirpichev
Copy link
Member Author

we should provide a support for long double complex in struct module.

But we don't have support for long double here. I don't know for sure why it wasn't here historically, will dig into it.

Probably together with support for long double - a support for long double complex will also make sense.

@efimov-mikhail
Copy link
Contributor

Even long double is not supported?
Probably, there is not by intention, just because a lack of time.

It seems like support for long double complex now is partial.
In come cases they are allowed (call csqrtl, for example), but in other they aren't (struct module).
I've just wanted to emphasize this.

@skirpichev
Copy link
Member Author

Probably, there is not by intention

Maybe is. The long double is a more complex beast: https://en.wikipedia.org/wiki/Long_double

@vstinner
Copy link
Member

vstinner commented Oct 10, 2024

I would prefer to not support long double, it looks complicated for little usage.

@skirpichev
Copy link
Member Author

@vstinner, I don't think it's too complicated. On technical side we, probably, have to add only PyFloat_Pack/Unpack16() and PyFloat_Pack/Unpack10() functions for the IEEE 754 quadruples and the x86 extended precision format. Say, "m" and "o" format codes for the struct module.

  • "m" - like "f" and "d" will use IEEE binary128 for the packed representation;
  • "o" - like "e" will use x86 extended precision format for native conversion as well.

These are popular standards and it's nice to have conversion helpers, available in the Python.

The "F" format for the long double complex type will be implemented like "E" and "C", i.e. using IEEE binary128.

If that sounds, I'll open an issue. Hardly discussion on d.p.o does make sense.

skirpichev added a commit to skirpichev/cpython that referenced this issue Mar 29, 2025
AA-Turner added a commit that referenced this issue Apr 1, 2025
Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type-feature A feature request or enhancement
Projects
None yet
Development

No branches or pull requests

3 participants