Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
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.

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
23 changes: 16 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 @@ -388,7 +389,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 @@ -398,7 +399,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 @@ -442,7 +443,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 @@ -970,6 +971,14 @@ are always available. They are listed here in alphabetical order.
.. audit-event:: builtins.id id id


.. class:: imaginary(x=0.0)

Return an imaginary number with the value ``float(x)*1j``. If argument is
omitted, returns ``0j``.

.. 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 @@ -1297,8 +1297,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
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 @@ -89,7 +89,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 @@ -101,7 +101,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 = {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, _nil=[]):

_atomic_types = {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
86 changes: 83 additions & 3 deletions Lib/test/test_capi/test_complex.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ class BadComplex3:
def __complex__(self):
raise RuntimeError

class ImaginarySubclass(imaginary):
pass


class CAPIComplexTest(ComplexesAreIdenticalMixin, unittest.TestCase):
def test_check(self):
Expand Down Expand Up @@ -177,7 +180,14 @@ def test_py_cr_sum(self):
# Test _Py_cr_sum()
_py_cr_sum = _testinternalcapi._py_cr_sum

self.assertComplexesAreIdentical(_py_cr_sum(-0j, -0.0)[0],
self.assertComplexesAreIdentical(_py_cr_sum(-0.0 - 0j, -0.0)[0],
complex(-0.0, -0.0))

def test_py_ci_sum(self):
# Test _Py_cr_sum()
_py_ci_sum = _testinternalcapi._py_ci_sum

self.assertComplexesAreIdentical(_py_ci_sum(-0.0 - 0j, -0.0)[0],
complex(-0.0, -0.0))

def test_py_c_diff(self):
Expand All @@ -190,7 +200,14 @@ def test_py_cr_diff(self):
# Test _Py_cr_diff()
_py_cr_diff = _testinternalcapi._py_cr_diff

self.assertComplexesAreIdentical(_py_cr_diff(-0j, 0.0)[0],
self.assertComplexesAreIdentical(_py_cr_diff(-0.0 - 0j, 0.0)[0],
complex(-0.0, -0.0))

def test_py_ci_diff(self):
# Test _Py_ci_diff()
_py_ci_diff = _testinternalcapi._py_ci_diff

self.assertComplexesAreIdentical(_py_ci_diff(-0.0 - 0j, 0.0)[0],
complex(-0.0, -0.0))

def test_py_rc_diff(self):
Expand All @@ -200,6 +217,13 @@ def test_py_rc_diff(self):
self.assertComplexesAreIdentical(_py_rc_diff(-0.0, 0j)[0],
complex(-0.0, -0.0))

def test_py_ic_diff(self):
# Test _Py_ic_diff()
_py_ic_diff = _testinternalcapi._py_ic_diff

self.assertComplexesAreIdentical(_py_ic_diff(-0.0, 0j)[0],
complex(-0.0, -0.0))

def test_py_c_neg(self):
# Test _Py_c_neg()
_py_c_neg = _testcapi._py_c_neg
Expand All @@ -219,6 +243,13 @@ def test_py_cr_prod(self):
self.assertComplexesAreIdentical(_py_cr_prod(complex('inf+1j'), INF)[0],
complex('inf+infj'))

def test_py_ci_prod(self):
# Test _Py_ci_prod()
_py_ci_prod = _testinternalcapi._py_ci_prod

self.assertComplexesAreIdentical(_py_ci_prod(complex('inf+1j'), INF)[0],
complex('-inf+infj'))

def test_py_c_quot(self):
# Test _Py_c_quot()
_py_c_quot = _testcapi._py_c_quot
Expand Down Expand Up @@ -248,13 +279,27 @@ def test_py_cr_quot(self):
self.assertComplexesAreIdentical(_py_cr_quot(complex('inf+1j'), 2**1000)[0],
INF + 2**-1000*1j)

def test_py_ci_quot(self):
# Test _Py_ci_quot()
_py_ci_quot = _testinternalcapi._py_ci_quot

self.assertComplexesAreIdentical(_py_ci_quot(complex('1+infj'), 2**1000)[0],
INF - 2**-1000*1j)

def test_py_rc_quot(self):
# Test _Py_rc_quot()
_py_rc_quot = _testinternalcapi._py_rc_quot

self.assertComplexesAreIdentical(_py_rc_quot(1.0, complex('nan-infj'))[0],
0j)

def test_py_ic_quot(self):
# Test _Py_ic_quot()
_py_ic_quot = _testinternalcapi._py_ic_quot

self.assertComplexesAreIdentical(_py_ic_quot(1.0, complex('inf-nanj'))[0],
-0.0)

def test_py_c_pow(self):
# Test _Py_c_pow()
_py_c_pow = _testcapi._py_c_pow
Expand All @@ -276,7 +321,6 @@ def test_py_c_pow(self):
self.assertEqual(_py_c_pow(max_num, 2),
(complex(INF, INF), errno.ERANGE))


def test_py_c_abs(self):
# Test _Py_c_abs()
_py_c_abs = _testcapi._py_c_abs
Expand All @@ -295,5 +339,41 @@ def test_py_c_abs(self):
self.assertEqual(_py_c_abs(complex(*[DBL_MAX]*2))[1], errno.ERANGE)


class CAPIImaginaryTest(unittest.TestCase):
def test_check(self):
# Test PyImaginary_Check()
check = _testlimitedcapi.imaginary_check

self.assertTrue(check(2j))
self.assertTrue(check(ImaginarySubclass(2)))
self.assertFalse(check(ComplexSubclass(1+2j)))
self.assertFalse(check(Complex()))
self.assertFalse(check(3))
self.assertFalse(check(3.0))
self.assertFalse(check(object()))

# CRASHES check(NULL)

def test_checkexact(self):
# PyImaginary_CheckExact()
checkexact = _testlimitedcapi.imaginary_checkexact

self.assertTrue(checkexact(2j))
self.assertFalse(checkexact(ImaginarySubclass(2)))
self.assertFalse(checkexact(ComplexSubclass(1+2j)))
self.assertFalse(checkexact(Complex()))
self.assertFalse(checkexact(3))
self.assertFalse(checkexact(3.0))
self.assertFalse(checkexact(object()))

# CRASHES checkexact(NULL)

def test_fromdouble(self):
# Test PyImaginary_FromDouble()
fromdouble = _testlimitedcapi.imaginary_fromdouble

self.assertEqual(fromdouble(2.0), 2.0j)


if __name__ == "__main__":
unittest.main()
Loading
Loading