Skip to content

BUG: dtype(ctypes.c_longdouble_complex) results in dtype('complex64') #28360

Open

Description

@jorenham

Describe the issue:

Python 3.14 will come with new complex C types in ctypes:
https://docs.python.org/3.14/library/ctypes.html#ctypes.c_double_complex

But it looks like their char codes differ from NumPy's F/D/G ones:

>>> import ctypes as ct
>>> ct.c_float_complex._type_
'E'
>>> ct.c_double_complex._type_
'C'
>>> ct.c_longdouble_complex._type_
'F'

Unfortunately, the last F clashes with np.complex64 char code, which causes dtype(ct.c_longdouble_complex) to return a complex64 dtype.
Passing the other two (E and C) ctypes to dtype will raise a TypeError, which at least isn't incorrect (although it might be nice to add support for those as well).

Reproduce the code example:

>>> import ctypes as ct; import numpy as np
>>> np.dtype(ct.c_longdouble_complex)
dtype('complex64')

Python and NumPy Versions:

>>> import sys, numpy; print(numpy.__version__); print(sys.version)
2.2.3
3.14.0a5 (main, Feb 20 2025, 02:53:28) [GCC 13.1.0]

Runtime Environment:

>>> import numpy; numpy.show_runtime()
[{'numpy_version': '2.2.3',
  'python': '3.14.0a5 (main, Feb 20 2025, 02:53:28) [GCC 13.1.0]',
  'uname': uname_result(system='Linux', node='pop-os', release='6.9.3-76060903-generic', version='#202405300957~1738770968~22.04~d5f7c84 SMP PREEMPT_DYNAMIC Wed F', machine='x86_64')},
 {'simd_extensions': {'baseline': ['SSE', 'SSE2', 'SSE3'],
                      'found': ['SSSE3',
                                'SSE41',
                                'POPCNT',
                                'SSE42',
                                'AVX',
                                'F16C',
                                'FMA3',
                                'AVX2'],
                      'not_found': ['AVX512F',
                                    'AVX512CD',
                                    'AVX512_SKX',
                                    'AVX512_CLX',
                                    'AVX512_CNL',
                                    'AVX512_ICL']}},
 {'architecture': 'Prescott',
  'filepath': '/usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.20.so',
  'internal_api': 'openblas',
  'num_threads': 32,
  'prefix': 'libopenblas',
  'threading_layer': 'pthreads',
  'user_api': 'blas',
  'version': '0.3.20'}]

Context for the issue:

I consider this a low-priority issue:
Python 3.14 isn't released yet, and is currently in alpha.
Also, I suppose that constructing a dtype this way is probably the 2nd most obscure (1st place goes to np.dtype('\x10') IMO).

Activity

  1. hpkfft commented on Apr 10, 2026

    @hpkfft
    Contributor

    Using Python 3.15 alpha, I see the following:

    >>> import ctypes as ct
    >>> ct.c_float_complex._type_
    'F'
    >>> ct.c_double_complex._type_
    'D'
    >>> ct.c_longdouble_complex._type_
    'G'
    
  2. jorenham commented on Apr 10, 2026

    @jorenham
    MemberAuthor

    Oh, huh, I see the same thing with Python 3.14. It was probably changed between 3.14 alpha and 3.14 final then.

  3. hpkfft commented on May 8, 2026

    @hpkfft
    Contributor

    Python 3.15.0b1 has made changes to be better compatible with NumPy. See #31204 and the Python discussion for details.
    In summary:

    Python 3.14:

    • struct: 'F'|'D'
    • array: none
    • memoryview: none
    • ctypes: 'F'|'D'|'G'

    Python 3.15:

    • struct: 'F'|'D' and 'Zf'|'Zd'
    • array: 'Zf'|'Zd'
    • memoryview: 'Zf'|'Zd'
    • ctypes: 'Zf'|'Zd'|'Zg'

    In particular, the following works with Python 3.15.0b1 and NumPy 2.4.4 :

    >>> import ctypes
    >>> import numpy as np
    >>> a = np.array([1, 2, 3], dtype=np.complex128)
    >>> m = memoryview(a)
    >>> m.format
    'Zd'
    >>> m.tolist()
    [(1+0j), (2+0j), (3+0j)]
    
    >>> np.array((ctypes.c_double_complex * 3)())
    array([0.+0.j, 0.+0.j, 0.+0.j])
    

    However,

    >>> ctypes.c_float_complex._type_
    'Zf'
    >>> ctypes.c_double_complex._type_
    'Zd'
    >>> ctypes.c_longdouble_complex._type_
    'Zg'
    

    so

    >>> np.dtype(ctypes.c_double_complex)
    Traceback (most recent call last):
      File "<python-input-13>", line 1, in <module>
        np.dtype(ctypes.c_double_complex)
        ~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.15/site-packages/numpy/_core/_dtype_ctypes.py", line 117, in dtype_from_ctypes_type
        return _from_ctypes_scalar(t)
      File "/usr/local/lib/python3.15/site-packages/numpy/_core/_dtype_ctypes.py", line 81, in _from_ctypes_scalar
        return np.dtype('<' + t._type_)
               ~~~~~~~~^^^^^^^^^^^^^^^^
    TypeError: data type '<Zd' not understood
    

    I think it would be nice for NumPy to support the PEP 3118 complex format strings in the same way it supports np.dtype('D'). If you agree, you may with to reopen this issue.

    Currently, a Python standard library array can be made with array.array('Zd', [1+2j, 3+4j]) but not with 'D'.
    Currently, a NumPy array can be made with np.array([1+2j, 3+4j], dtype='D') but not with Zd:

    >>> np.array([1+2j, 3+4j], dtype='Zd')
    Traceback (most recent call last):
      File "<python-input-25>", line 1, in <module>
        np.array([1+2j, 3+4j], dtype='Zd')
        ~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^
    TypeError: data type 'Zd' not understood
    
  4. Abhi210 commented on May 9, 2026

    @Abhi210
    Contributor

    I looked into this issue on Python 3.15.0b1 and found a potential fix.

    The root cause seems to be that Python 3.15 switched ctypes complex scalar _type_ codes to PEP 3118 format strings:

    ctypes.c_float_complex._type_      -> 'Zf'
    ctypes.c_double_complex._type_     -> 'Zd'
    ctypes.c_longdouble_complex._type_ -> 'Zg'

    However, NumPy currently does not recognize these format strings in dtype parsing, so calls like:

    np.dtype(ctypes.c_double_complex)
    np.dtype("Zd")

    fail with:

    TypeError: data type '<Zd' not understood

    I tested a fix by adding support for the PEP 3118 complex format strings (Zf, Zd, Zg) in two places.

    First, in numpy/_core/_internal.py, I extended _pep3118_standard_map:

    _pep3118_standard_map = {
        '?': '?',
        'c': 'S1',
        'b': 'b',
        'B': 'B',
        'h': 'i2',
        'H': 'u2',
        'i': 'i4',
        'I': 'u4',
        'l': 'i4',
        'L': 'u4',
        'q': 'i8',
        'Q': 'u8',
        'e': 'f2',
        'f': 'f',
        'd': 'd',
        'Zf': 'F',
        'Zd': 'D',
        'Zg': 'G',  # I added this missing piece 
        's': 'S',
        'w': 'U',
        'O': 'O',
        'x': 'V',  # padding
    }

    Then, in numpy/_core/src/multiarray/descriptor.c, I added handling for Z* complex format strings inside _convert_from_str(PyObject *obj, int align):

    else if (len == 2 && type[0] == 'Z') {
        switch (type[1]) {
            case 'f': check_num = NPY_CFLOAT; break;
            case 'd': check_num = NPY_CDOUBLE; break;
            case 'g': check_num = NPY_CLONGDOUBLE; break;
            default: goto fail;
        }
    }

    With these changes, the following now works correctly on Python 3.15.0b1:

    >>> import ctypes
    >>> import numpy as np
    
    >>> a = np.array([1, 2, 3], dtype=np.complex128)
    >>> m = memoryview(a)
    
    >>> m.format
    'Zd'
    
    >>> m.tolist()
    [(1+0j), (2+0j), (3+0j)]
    
    >>> np.array((ctypes.c_double_complex * 3)())
    array([0.+0.j, 0.+0.j, 0.+0.j])
    
    >>> ctypes.c_float_complex._type_
    'Zf'
    
    >>> ctypes.c_double_complex._type_
    'Zd'
    
    >>> ctypes.c_longdouble_complex._type_
    'Zg'
    
    >>> np.dtype(ctypes.c_double_complex)
    dtype('complex128')
    
    >>> np.array([1+2j, 3+4j], dtype='Zd')
    array([1.+2.j, 3.+4.j])

    Environment:

    NumPy:  2.5.0.dev0+git20260508.3bea241
    Python: 3.15.0b1

    This approach also enables direct support for PEP 3118-compliant complex format strings in np.dtype(...), improving interoperability with ctypes, memoryview, and array.array.

    PS: ChatGPT was used to format this comment structure into a readable form. Gemini was used as an assistant to understand certain parts of the code and to rewrite the logic in better way.

  5. added this to the 2.5.0 Release milestone on May 11, 2026
  6. added
    triage reviewIssue/PR to be discussed at the next triage meeting
    on May 11, 2026
  7. added a commit that references this issue on Aug 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    00 - Bugtriage reviewIssue/PR to be discussed at the next triage meeting

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions