Skip to main content

Objects/complexobject.c (part 12)

Source:

cpython 3.14 @ ab2d84fe1023/Objects/complexobject.c

This annotation covers power, absolute value, and hash for complex numbers. See objects_complexobject11_detail for arithmetic and comparison operations.

Map

LinesSymbolRole
1-80complex.__pow__Complex exponentiation
81-160complex.__abs__Modulus (magnitude)
161-240complex.conjugateReturn the complex conjugate
241-400complex.__hash__Hash consistent with float.__hash__

Reading

complex.__pow__

// CPython: Objects/complexobject.c:380 complex_pow
static PyObject *
complex_pow(PyObject *v, PyObject *w, PyObject *z)
{
Py_complex a, b, p;
TO_COMPLEX(v, a);
TO_COMPLEX(w, b);

if (b.real == 0.0 && b.imag == 0.0)
return PyComplex_FromCComplex((Py_complex){1.0, 0.0});
if (a.real == 0.0 && a.imag == 0.0) {
if (b.real < 0.0)
PyErr_SetString(PyExc_ZeroDivisionError, "0.0 to a negative power");
return PyComplex_FromCComplex((Py_complex){0.0, 0.0});
}
/* General: a**b = exp(b * log(a)) */
Py_complex log_a = c_log(a);
Py_complex b_log_a = {b.real * log_a.real - b.imag * log_a.imag,
b.real * log_a.imag + b.imag * log_a.real};
p = c_exp(b_log_a);
return PyComplex_FromCComplex(p);
}

Complex power uses the identity a**b = exp(b * log(a)). Special cases handle b == 0 (returns 1+0j) and a == 0 with negative real exponent (raises ZeroDivisionError). The three-argument form pow(a, b, mod) is not supported for complex numbers.

complex.__abs__

// CPython: Objects/complexobject.c:440 complex_abs
static PyObject *
complex_abs(PyObject *v)
{
double result;
Py_complex c;
TO_COMPLEX(v, c);
PyFPE_START_PROTECT("complex_abs", return 0);
result = c_abs(c); /* hypot(real, imag) */
PyFPE_END_PROTECT(result);
return PyFloat_FromDouble(result);
}

abs(3+4j) returns 5.0. c_abs calls hypot(real, imag) which avoids overflow for large values. The result is always a float, never a complex.

complex.conjugate

// CPython: Objects/complexobject.c:462 complex_conjugate
static PyObject *
complex_conjugate(PyObject *self, PyObject *Py_UNUSED(ignored))
{
Py_complex c;
TO_COMPLEX(self, c);
c.imag = -c.imag;
return PyComplex_FromCComplex(c);
}

(3+4j).conjugate() returns (3-4j). The real part is unchanged; only the imaginary part is negated. Used in inner products and Fourier transforms.

complex.__hash__

// CPython: Objects/complexobject.c:510 complex_hash
static Py_hash_t
complex_hash(PyObject *v)
{
Py_uhash_t hashreal, hashimag, combined;
Py_complex c;
TO_COMPLEX(v, c);
/* Hash real part as float */
hashreal = (Py_uhash_t)_Py_HashDouble((PyObject *)v, c.real);
if (hashreal == (Py_uhash_t)-1) return -1;
/* Hash imaginary part; if zero, hash of 0j == hash of 0 */
hashimag = (Py_uhash_t)_Py_HashDouble((PyObject *)v, c.imag);
if (hashimag == (Py_uhash_t)-1) return -1;
combined = hashreal + _PyHASH_IMAG * hashimag;
if (combined == (Py_uhash_t)-1) combined = (Py_uhash_t)-2;
return (Py_hash_t)combined;
}

hash(3+0j) == hash(3) == hash(3.0) — the numeric hash invariant requires that equal numbers have equal hashes across all numeric types. _PyHASH_IMAG is a fixed constant mixing the imaginary contribution. If the imaginary part is 0.0, hashimag is 0 and combined == hashreal.

gopy notes

complex.__pow__ is objects.ComplexPow in objects/complexobject.go; uses cmplx.Log and cmplx.Exp from Go's math/cmplx. complex.__abs__ calls cmplx.Abs. conjugate is objects.ComplexConjugate. complex.__hash__ mirrors CPython's numeric hash invariant using objects.HashFloat for each component.