Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
7110869
Imaginary type and IEC 60559-compatible complex arithmetic
skirpichev Apr 23, 2024
9186fd6
Merge branch 'master' into imaginary-class-109218
skirpichev Oct 20, 2025
ca6ab22
Cleanup: don't expose imaginary as built-in
skirpichev Oct 20, 2025
9f1481a
Merge branch 'master' into imaginary-class-109218
skirpichev Oct 21, 2025
66a84dd
XXX C-API
skirpichev Oct 21, 2025
85af258
Merge branch 'main' into imaginary-class-109218
skirpichev Oct 29, 2025
7508590
Merge branch 'master' into imaginary-class-109218
skirpichev Oct 31, 2025
ab1ecc3
imaginary: accept imaginary input
skirpichev Oct 31, 2025
023d357
Merge branch 'master' into imaginary-class-109218
skirpichev Oct 31, 2025
ddc52c1
Amend ca6ab2292ac: fix pickling
skirpichev Oct 31, 2025
55e8b16
Merge branch 'main' into imaginary-class-109218
skirpichev Nov 4, 2025
84235a0
Merge branch 'main' into imaginary-class-109218
skirpichev Nov 5, 2025
e554fa1
Increment marshal version
skirpichev Nov 5, 2025
f19bf28
Merge branch 'master' into imaginary-class-109218
skirpichev Nov 6, 2025
2fac9a9
Merge branch 'master' into imaginary-class-109218
skirpichev Nov 13, 2025
01bf5d4
Merge branch 'main' into imaginary-class-109218
skirpichev Nov 19, 2025
781f032
Merge branch 'master' into imaginary-class-109218
skirpichev Nov 20, 2025
4d138ee
Revert "Cleanup: don't expose imaginary as built-in"
skirpichev Nov 20, 2025
ce86994
cleanup docs, revert ab1ecc31673
skirpichev Nov 20, 2025
39f81bf
reject complex "literals" as values of Constant nodes
skirpichev Nov 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions Doc/c-api/complex.rst
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,42 @@ Complex Number Objects
Use :meth:`~object.__index__` if available.


Imaginary Number Objects
^^^^^^^^^^^^^^^^^^^^^^^^

.. index:: pair: object; imaginary number


.. c:type:: PyImaginaryObject

This subtype of :c:type:`PyComplexObject` represents a Python imaginary
number object.


.. c:var:: PyTypeObject PyImaginary_Type

This instance of :c:type:`PyTypeObject` represents purely imaginary numbers,
the Python complex number type *without* real component.


.. c:function:: int PyImaginary_Check(PyObject *p)

Return true if its argument is a :c:type:`PyImaginaryObject` or a subtype of
:c:type:`PyImaginaryObject`. This function always succeeds.


.. c:function:: int PyImaginary_CheckExact(PyObject *p)

Return true if its argument is a :c:type:`PyImaginaryObject`, but not a
subtype of :c:type:`PyImaginaryObject`. This function always succeeds.


.. c:function:: PyObject* PyImaginary_FromDouble(double imag)

Return a new :c:type:`PyImaginaryObject` object with *imag* imaginary
component. Return ``NULL`` with an exception set on error.


Complex Numbers as C Structures
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down
2 changes: 2 additions & 0 deletions Doc/data/stable_abi.dat

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Doc/library/ast.rst
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ Literals

A constant value. The ``value`` attribute of the ``Constant`` literal contains the
Python object it represents. The values represented can be instances of :class:`str`,
:class:`bytes`, :class:`int`, :class:`float`, :class:`complex`, and :class:`bool`,
:class:`bytes`, :class:`int`, :class:`float`, :class:`imaginary`, and :class:`bool`,
and the constants :data:`None` and :data:`Ellipsis`.

.. doctest::
Expand Down
4 changes: 2 additions & 2 deletions Doc/library/cmath.rst
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ Constants
.. data:: infj

Complex number with zero real part and positive infinity imaginary
part. Equivalent to ``complex(0.0, float('inf'))``.
part. Equivalent to ``float('inf')*1j``.

.. versionadded:: 3.6

Expand All @@ -346,7 +346,7 @@ Constants
.. data:: nanj

Complex number with zero real part and NaN imaginary part. Equivalent to
``complex(0.0, float('nan'))``.
``float('nan')*1j``.

.. versionadded:: 3.6

Expand Down
24 changes: 17 additions & 7 deletions Doc/library/functions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,11 @@ are always available. They are listed here in alphabetical order.
| | :func:`complex` | | | | **P** | | **V** |
| | | | **I** | | :func:`pow` | | :func:`vars` |
| | **D** | | :func:`id` | | :func:`print` | | |
| | :func:`delattr` | | :func:`input` | | :func:`property` | | **Z** |
| | |func-dict|_ | | :func:`int` | | | | :func:`zip` |
| | :func:`dir` | | :func:`isinstance` | | | | |
| | :func:`divmod` | | :func:`issubclass` | | | | **_** |
| | :func:`delattr` | | :func:`imaginary` | | :func:`property` | | |
| | |func-dict|_ | | :func:`input` | | | | **Z** |
| | :func:`dir` | | :func:`int` | | | | :func:`zip` |
| | :func:`divmod` | | :func:`isinstance` | | | | |
| | | | :func:`issubclass` | | | | **_** |
| | | | :func:`iter` | | | | :func:`__import__` |
+-------------------------+-----------------------+-----------------------+-------------------------+

Expand Down Expand Up @@ -395,7 +396,7 @@ are always available. They are listed here in alphabetical order.
>>> complex('+1.23')
(1.23+0j)
>>> complex('-4.5j')
-4.5j
(0.0-4.5j)
>>> complex('-1.23+4.5j')
(-1.23+4.5j)
>>> complex('\t( -1.23+4.5J )\n')
Expand All @@ -405,7 +406,7 @@ are always available. They are listed here in alphabetical order.
>>> complex(1.23)
(1.23+0j)
>>> complex(imag=-4.5)
-4.5j
(0.0-4.5j)
>>> complex(-1.23, 4.5)
(-1.23+4.5j)

Expand Down Expand Up @@ -449,7 +450,7 @@ are always available. They are listed here in alphabetical order.

See also :meth:`complex.from_number` which only accepts a single numeric argument.

If all arguments are omitted, returns ``0j``.
If all arguments are omitted, returns ``0.0+0j``.

The complex type is described in :ref:`typesnumeric`.

Expand Down Expand Up @@ -977,6 +978,15 @@ are always available. They are listed here in alphabetical order.
.. audit-event:: builtins.id id id


.. class:: imaginary(x=0.0)

Create an imaginary number from a number or a string.

This is equivalent of ``float(x)*1j``.

.. versionadded:: next


.. function:: input()
input(prompt, /)

Expand Down
4 changes: 2 additions & 2 deletions Doc/reference/lexical_analysis.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1363,8 +1363,8 @@ Imaginary literals

Python has :ref:`complex number <typesnumeric>` objects, but no complex
literals.
Instead, *imaginary literals* denote complex numbers with a zero
real part.
Instead, *imaginary literals* denote complex numbers without a real part, an instance
of :class:`imaginary`.

For example, in math, the complex number 3+4.2\ *i* is written
as the real number 3 added to the imaginary number 4.2\ *i*.
Expand Down
6 changes: 6 additions & 0 deletions Include/complexobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,21 @@ extern "C" {
/* Complex object interface */

PyAPI_DATA(PyTypeObject) PyComplex_Type;
PyAPI_DATA(PyTypeObject) PyImaginary_Type;

#define PyComplex_Check(op) PyObject_TypeCheck((op), &PyComplex_Type)
#define PyComplex_CheckExact(op) Py_IS_TYPE((op), &PyComplex_Type)

#define PyImaginary_Check(op) PyObject_TypeCheck((op), &PyImaginary_Type)
#define PyImaginary_CheckExact(op) Py_IS_TYPE((op), &PyImaginary_Type)

PyAPI_FUNC(PyObject *) PyComplex_FromDoubles(double real, double imag);

PyAPI_FUNC(double) PyComplex_RealAsDouble(PyObject *op);
PyAPI_FUNC(double) PyComplex_ImagAsDouble(PyObject *op);

PyAPI_FUNC(PyObject *) PyImaginary_FromDouble(double imag);

#ifndef Py_LIMITED_API
# define Py_CPYTHON_COMPLEXOBJECT_H
# include "cpython/complexobject.h"
Expand Down
2 changes: 2 additions & 0 deletions Include/cpython/complexobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ typedef struct {
Py_complex cval;
} PyComplexObject;

#define PyImaginaryObject PyComplexObject

PyAPI_FUNC(PyObject *) PyComplex_FromCComplex(Py_complex);

PyAPI_FUNC(Py_complex) PyComplex_AsCComplex(PyObject *op);
2 changes: 1 addition & 1 deletion Include/cpython/marshal.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ PyAPI_FUNC(PyObject *) PyMarshal_ReadObjectFromString(const char *,
Py_ssize_t);
PyAPI_FUNC(PyObject *) PyMarshal_WriteObjectToString(PyObject *, int);

#define Py_MARSHAL_VERSION 5
#define Py_MARSHAL_VERSION 6

PyAPI_FUNC(long) PyMarshal_ReadLongFromFile(FILE *);
PyAPI_FUNC(int) PyMarshal_ReadShortFromFile(FILE *);
Expand Down
6 changes: 6 additions & 0 deletions Include/internal/pycore_complexobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,17 @@ extern int _PyComplex_FormatAdvancedWriter(

// Operations on complex numbers.
PyAPI_FUNC(Py_complex) _Py_cr_sum(Py_complex, double);
PyAPI_FUNC(Py_complex) _Py_ci_sum(Py_complex, double);
PyAPI_FUNC(Py_complex) _Py_cr_diff(Py_complex, double);
PyAPI_FUNC(Py_complex) _Py_rc_diff(double, Py_complex);
PyAPI_FUNC(Py_complex) _Py_ci_diff(Py_complex, double);
PyAPI_FUNC(Py_complex) _Py_ic_diff(double, Py_complex);
PyAPI_FUNC(Py_complex) _Py_cr_prod(Py_complex, double);
PyAPI_FUNC(Py_complex) _Py_ci_prod(Py_complex, double);
PyAPI_FUNC(Py_complex) _Py_cr_quot(Py_complex, double);
PyAPI_FUNC(Py_complex) _Py_rc_quot(double, Py_complex);
PyAPI_FUNC(Py_complex) _Py_ci_quot(Py_complex, double);
PyAPI_FUNC(Py_complex) _Py_ic_quot(double, Py_complex);


#ifdef __cplusplus
Expand Down
4 changes: 2 additions & 2 deletions Lib/ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def _convert_literal(node):
isinstance(node, UnaryOp)
and isinstance(node.op, (UAdd, USub))
and isinstance(node.operand, Constant)
and type(operand := node.operand.value) in (int, float, complex)
and type(operand := node.operand.value) in (int, float, imaginary)
):
if isinstance(node.op, UAdd):
return + operand
Expand All @@ -102,7 +102,7 @@ def _convert_literal(node):
and isinstance(node.left, (Constant, UnaryOp))
and isinstance(node.right, Constant)
and type(left := _convert_literal(node.left)) in (int, float)
and type(right := _convert_literal(node.right)) is complex
and type(right := _convert_literal(node.right)) is imaginary
):
if isinstance(node.op, Add):
return left + right
Expand Down
5 changes: 3 additions & 2 deletions Lib/copy.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ def copy(x):


_copy_atomic_types = frozenset({types.NoneType, int, float, bool, complex, str, tuple,
bytes, frozenset, type, range, slice, property,
imaginary, bytes, frozenset, type, range, slice, property,
types.BuiltinFunctionType, types.EllipsisType,
types.NotImplementedType, types.FunctionType, types.CodeType,
weakref.ref, super})
Expand Down Expand Up @@ -164,7 +164,8 @@ def deepcopy(x, memo=None):

_atomic_types = frozenset({types.NoneType, types.EllipsisType, types.NotImplementedType,
int, float, bool, complex, bytes, str, types.CodeType, type, range,
types.BuiltinFunctionType, types.FunctionType, weakref.ref, property})
types.BuiltinFunctionType, types.FunctionType, weakref.ref,
property, imaginary})

_deepcopy_dispatch = d = {}

Expand Down
12 changes: 6 additions & 6 deletions Lib/test/test_bytes.py
Original file line number Diff line number Diff line change
Expand Up @@ -785,12 +785,12 @@ def __int__(self):
('%X format: an integer is required, not float', b'%X', 2.11),
('%o format: an integer is required, not float', b'%o', 1.79),
('%x format: an integer is required, not PseudoFloat', b'%x', pi),
('%x format: an integer is required, not complex', b'%x', 3j),
('%X format: an integer is required, not complex', b'%X', 2j),
('%o format: an integer is required, not complex', b'%o', 1j),
('%u format: a real number is required, not complex', b'%u', 3j),
('%i format: a real number is required, not complex', b'%i', 2j),
('%d format: a real number is required, not complex', b'%d', 2j),
('%x format: an integer is required, not complex', b'%x', 1+3j),
('%X format: an integer is required, not complex', b'%X', 1+2j),
('%o format: an integer is required, not complex', b'%o', 1+1j),
('%u format: a real number is required, not complex', b'%u', 1+3j),
('%i format: a real number is required, not complex', b'%i', 1+2j),
('%d format: a real number is required, not complex', b'%d', 1+2j),
(
r'%c requires an integer in range\(256\)'
r' or a single byte, not .*\.PseudoFloat',
Expand Down
Loading
Loading