-
-
Notifications
You must be signed in to change notification settings - Fork 31.8k
Add support for C99 complex type (_Complex) to the struct module #121249
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
Comments
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>
Implemented by change 7487db4. |
IMO, 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. |
Even long double is not supported? It seems like support for long double complex now is partial. |
Maybe is. The long double is a more complex beast: https://en.wikipedia.org/wiki/Long_double |
I would prefer to not support long double, it looks complicated for little usage. |
@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.
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. |
Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com>
@skirpichev @vstinner Is the choice of the "E" and "C" something that could be reconsidered? The ~20 years old NumPy project have been using the typecodes "F" for In [1]: import numpy as np
In [2]: np.typecodes
Out[2]:
{'Character': 'c',
'Integer': 'bhilqnp',
'UnsignedInteger': 'BHILQNP',
'Float': 'efdg',
'Complex': 'FDG',
'AllInteger': 'bBhHiIlLqQnNpP',
'AllFloat': 'efdgFDG',
'Datetime': 'Mm',
'All': '?bhilqnpBHILQNPefdgFDGSUVOMm'}
In [3]: np.typename("F")
Out[3]: 'complex single precision'
In [4]: np.typename("D")
Out[4]: 'complex double precision'
In [5]: np.typename("G")
Out[5]: 'complex long double precision' On a somewhat less urgent matter, tying the availability of these format characters in PS: If support for |
@dalcinl, thanks. Yes, I think it could be reconsidered. Codes for the struct module chosen to be consistent wrt codes in the ctypes module. Both could and, probably, should be adjusted. @vstinner ?
Maybe it does make sense for the struct module, but not for the ctypes (as the standard doesn't say anything about directly passing a complex as argument or return value to a function call).
Not with struct (as structs aren't arrays - they can have padding between members). |
Oh, I missed that. If ctypes is using E and C, we cannot change that for backward compatibility reasons, but maybe we can make ctypes accept F and D on input. EDIT: Or maybe we can? Should
You are definitely right.
You may be technically right, but I think that every compiler out there satisfies |
Using the same format letter in ctypes and numpy sounds like a good idea. Python 3.14.0 final is not released yet, so we can still change ctypes formats. |
No, it's still an option as ctypes support also will be available only in 3.14.
This slot referenced in sphinx docs, though not actual type codes.
Yeah, I don't know a real-life example, when it's wrong. But this expectation is not backed by the standard. |
Good, I'll prepare a patch. |
…ctypes modules F - for float _Complex D - for double _Complex G - for long double _Complex (not supported by the struct module)
PR is ready for review: #132827 I'll prepare another to enable complex types unconditionally for the struct module. |
Regarding |
We can, but this support requires complex support on the compiler side.
The problem is passing arguments (or return values). For complex types it's usually not just a pointer to array of suitable real type.
That does make sense, but only for the ctypes module. |
Yes, it requires complex support when the FFI library is built, but not when CPython is built (if using a different compiler).
But if the FFI library have complex support, it knows how to do it. Does python still need complex support from the C compiler? If yes, what for? What am I missing?
The point I'm trying to make is the following: No C code in CPython source tree needs to use |
…#132827) * F - for float _Complex * D - for double _Complex * G - for long double _Complex (not supported by the struct module)
It seems, no code actually uses these names, only sizes of the unnamed union members are important. Though, I think it's good to be here consistent wrt type codes ('g' for long double, etc). This amends 85f89cb.
…struct module Each complex type interpreted as an array type containing exactly two elements of the corresponding real type (real and imaginary parts, respectively).
Ok, here is a small pr that amends 85f89cb: #132863 PR with unconditional support for complex types in the struct module: #132864
You are correct for the struct module, see patch above. In principle, we could implement getters/setters in the ctypes module just like that (use arrays and indexing instead of CMPLX/creal/cimag). Feel free to submit a patch if you think this will simplify something (e.g. we can get rid of But I'm not sure if this worth: you can't pass arguments & unpack return values without support for complex types both from platform and libffi. I tried to explain why it so - above, here is another attempt: #61103 (comment) |
Patch for ctypes (on top of #132864) attached. It has some pros: we can remove a patchdiff --git a/Makefile.pre.in b/Makefile.pre.in
index 886bd7ae1c..fc1bd0d819 100644
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -3304,7 +3304,7 @@ MODULE_CMATH_DEPS=$(srcdir)/Modules/_math.h
MODULE_MATH_DEPS=$(srcdir)/Modules/_math.h
MODULE_PYEXPAT_DEPS=@LIBEXPAT_INTERNAL@
MODULE_UNICODEDATA_DEPS=$(srcdir)/Modules/unicodedata_db.h $(srcdir)/Modules/unicodename_db.h
-MODULE__CTYPES_DEPS=$(srcdir)/Modules/_ctypes/ctypes.h $(srcdir)/Modules/_complex.h
+MODULE__CTYPES_DEPS=$(srcdir)/Modules/_ctypes/ctypes.h
MODULE__CTYPES_TEST_DEPS=$(srcdir)/Modules/_ctypes/_ctypes_test_generated.c.h
MODULE__CTYPES_MALLOC_CLOSURE=@MODULE__CTYPES_MALLOC_CLOSURE@
MODULE__DECIMAL_DEPS=$(srcdir)/Modules/_decimal/docstrings.h @LIBMPDEC_INTERNAL@
diff --git a/Modules/_complex.h b/Modules/_complex.h
deleted file mode 100644
index 28d4a32794..0000000000
--- a/Modules/_complex.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/* Workarounds for buggy complex number arithmetic implementations. */
-
-#ifndef Py_HAVE_C_COMPLEX
-# error "this header file should only be included if Py_HAVE_C_COMPLEX is defined"
-#endif
-
-#include <complex.h>
-
-/* Other compilers (than clang), that claims to
- implement C11 *and* define __STDC_IEC_559_COMPLEX__ - don't have
- issue with CMPLX(). This is specific to glibc & clang combination:
- https://sourceware.org/bugzilla/show_bug.cgi?id=26287
-
- Here we fallback to using __builtin_complex(), available in clang
- v12+. Else CMPLX implemented following C11 6.2.5p13: "Each complex type
- has the same representation and alignment requirements as an array
- type containing exactly two elements of the corresponding real type;
- the first element is equal to the real part, and the second element
- to the imaginary part, of the complex number.
- */
-#if !defined(CMPLX)
-# if defined(__clang__) && __has_builtin(__builtin_complex)
-# define CMPLX(x, y) __builtin_complex ((double) (x), (double) (y))
-# define CMPLXF(x, y) __builtin_complex ((float) (x), (float) (y))
-# define CMPLXL(x, y) __builtin_complex ((long double) (x), (long double) (y))
-# else
-static inline double complex
-CMPLX(double real, double imag)
-{
- double complex z;
- ((double *)(&z))[0] = real;
- ((double *)(&z))[1] = imag;
- return z;
-}
-
-static inline float complex
-CMPLXF(float real, float imag)
-{
- float complex z;
- ((float *)(&z))[0] = real;
- ((float *)(&z))[1] = imag;
- return z;
-}
-
-static inline long double complex
-CMPLXL(long double real, long double imag)
-{
- long double complex z;
- ((long double *)(&z))[0] = real;
- ((long double *)(&z))[1] = imag;
- return z;
-}
-# endif
-#endif
diff --git a/Modules/_ctypes/_ctypes_test.c b/Modules/_ctypes/_ctypes_test.c
index 2268072545..557bfa85b1 100644
--- a/Modules/_ctypes/_ctypes_test.c
+++ b/Modules/_ctypes/_ctypes_test.c
@@ -24,7 +24,7 @@
#endif
#if defined(Py_HAVE_C_COMPLEX) && defined(Py_FFI_SUPPORT_C_COMPLEX)
-# include "../_complex.h" // csqrt()
+# include <complex.h> // csqrt()
# undef I // for _ctypes_test_generated.c.h
#endif
#include <stdio.h> // printf()
diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c
index 83471aa3a4..48589c9892 100644
--- a/Modules/_ctypes/callproc.c
+++ b/Modules/_ctypes/callproc.c
@@ -103,9 +103,6 @@ module _ctypes
#include "pycore_global_objects.h"// _Py_ID()
#include "pycore_traceback.h" // _PyTraceback_Add()
-#if defined(Py_HAVE_C_COMPLEX) && defined(Py_FFI_SUPPORT_C_COMPLEX)
-#include "../_complex.h" // complex
-#endif
#define clinic_state() (get_module_state(module))
#include "clinic/callproc.c.h"
#undef clinic_state
@@ -653,9 +650,9 @@ union result {
float f;
void *p;
#if defined(Py_HAVE_C_COMPLEX) && defined(Py_FFI_SUPPORT_C_COMPLEX)
- double complex D;
- float complex F;
- long double complex G;
+ double D[2];
+ float F[2];
+ long double G[2];
#endif
};
diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c
index 580ea18af2..d7e4bfdb82 100644
--- a/Modules/_ctypes/cfield.c
+++ b/Modules/_ctypes/cfield.c
@@ -14,10 +14,6 @@
#include <ffi.h>
#include "ctypes.h"
-#if defined(Py_HAVE_C_COMPLEX) && defined(Py_FFI_SUPPORT_C_COMPLEX)
-# include "../_complex.h" // complex
-#endif
-
#define CTYPES_CFIELD_CAPSULE_NAME_PYMEM "_ctypes/cfield.c pymem"
/*[clinic input]
@@ -768,13 +764,13 @@ d_get(void *ptr, Py_ssize_t size)
static PyObject *
D_set(void *ptr, PyObject *value, Py_ssize_t size)
{
- assert(NUM_BITS(size) || (size == sizeof(double complex)));
+ assert(NUM_BITS(size) || (size == 2*sizeof(double)));
Py_complex c = PyComplex_AsCComplex(value);
if (c.real == -1 && PyErr_Occurred()) {
return NULL;
}
- double complex x = CMPLX(c.real, c.imag);
+ double x[2] = {c.real, c.imag};
memcpy(ptr, &x, sizeof(x));
_RET(value);
}
@@ -782,24 +778,24 @@ D_set(void *ptr, PyObject *value, Py_ssize_t size)
static PyObject *
D_get(void *ptr, Py_ssize_t size)
{
- assert(NUM_BITS(size) || (size == sizeof(double complex)));
- double complex x;
+ assert(NUM_BITS(size) || (size == 2*sizeof(double)));
+ double x[2];
memcpy(&x, ptr, sizeof(x));
- return PyComplex_FromDoubles(creal(x), cimag(x));
+ return PyComplex_FromDoubles(x[0], x[1]);
}
/* F: float complex */
static PyObject *
F_set(void *ptr, PyObject *value, Py_ssize_t size)
{
- assert(NUM_BITS(size) || (size == sizeof(float complex)));
+ assert(NUM_BITS(size) || (size == 2*sizeof(float)));
Py_complex c = PyComplex_AsCComplex(value);
if (c.real == -1 && PyErr_Occurred()) {
return NULL;
}
- float complex x = CMPLXF((float)c.real, (float)c.imag);
+ float x[2] = {(float)c.real, (float)c.imag};
memcpy(ptr, &x, sizeof(x));
_RET(value);
}
@@ -807,24 +803,24 @@ F_set(void *ptr, PyObject *value, Py_ssize_t size)
static PyObject *
F_get(void *ptr, Py_ssize_t size)
{
- assert(NUM_BITS(size) || (size == sizeof(float complex)));
- float complex x;
+ assert(NUM_BITS(size) || (size == 2*sizeof(float)));
+ float x[2];
memcpy(&x, ptr, sizeof(x));
- return PyComplex_FromDoubles(crealf(x), cimagf(x));
+ return PyComplex_FromDoubles(x[0], x[1]);
}
/* G: long double complex */
static PyObject *
G_set(void *ptr, PyObject *value, Py_ssize_t size)
{
- assert(NUM_BITS(size) || (size == sizeof(long double complex)));
+ assert(NUM_BITS(size) || (size == 2*sizeof(long double)));
Py_complex c = PyComplex_AsCComplex(value);
if (c.real == -1 && PyErr_Occurred()) {
return NULL;
}
- long double complex x = CMPLXL(c.real, c.imag);
+ long double x[2] = {c.real, c.imag};
memcpy(ptr, &x, sizeof(x));
_RET(value);
}
@@ -832,11 +828,11 @@ G_set(void *ptr, PyObject *value, Py_ssize_t size)
static PyObject *
G_get(void *ptr, Py_ssize_t size)
{
- assert(NUM_BITS(size) || (size == sizeof(long double complex)));
- long double complex x;
+ assert(NUM_BITS(size) || (size == 2*sizeof(long double)));
+ long double x[2];
memcpy(&x, ptr, sizeof(x));
- return PyComplex_FromDoubles((double)creall(x), (double)cimagl(x));
+ return PyComplex_FromDoubles((double)x[0], (double)x[1]);
}
#endif
diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h
index 3b6d390728..30814dc43e 100644
--- a/Modules/_ctypes/ctypes.h
+++ b/Modules/_ctypes/ctypes.h
@@ -12,7 +12,6 @@
// Do we support C99 complex types in ffi?
// For Apple's libffi, this must be determined at runtime (see gh-128156).
#if defined(Py_HAVE_C_COMPLEX) && defined(Py_FFI_SUPPORT_C_COMPLEX)
-# include "../_complex.h" // complex
# if USING_APPLE_OS_LIBFFI && defined(__has_builtin)
# if __has_builtin(__builtin_available)
# define Py_FFI_COMPLEX_AVAILABLE __builtin_available(macOS 10.15, *)
@@ -494,9 +493,9 @@ struct tagPyCArgObject {
float f;
void *p;
#if defined(Py_HAVE_C_COMPLEX) && defined(Py_FFI_SUPPORT_C_COMPLEX)
- double complex D;
- float complex F;
- long double complex G;
+ double D[2];
+ float F[2];
+ long double G[2];
#endif
} value;
PyObject *obj; |
Your patch looks great! I think you can still get rid of |
…struct module Each complex type interpreted as an array type containing exactly two elements of the corresponding real type (real and imaginary parts, respectively).
…e 10) This amends 85f89cb.
Feature or enhancement
Proposal:
The struct module has support for
float
anddouble
types, so at least there should be alsofloat _Complex
anddouble _Complex
. I'll work on a patch.Initial version
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
complex
types instruct
#132864The text was updated successfully, but these errors were encountered: