Complex Elementary Functions and Branch Cuts

Overview

sangi's Complex<T> provides exp, log, trigonometric and hyperbolic functions and their inverses, powers, and $n$-th roots. All return principal values, and the branch-cut placement follows the same conventions as std::complex and Python cmath.

Every function is built on the real-valued trigonometric and exponential functions of T. For T = double, the <cmath> functions are called; for multiprecision types, T::sin, T::cos, T::exp, T::log, etc. are invoked. Multiprecision accuracy follows the precision configured for the internal scalar T.

For the API listing, see Complex API Reference — Math Functions.

exp and log

exp

The complex exponential is built directly from Euler's formula:

$$e^{a+bi} = e^a (\cos b + i \sin b)$$

The implementation invokes the real exp(a) once and cos(b) / sin(b) once each. Accuracy degradation when $|b|$ is large depends on the quality of range reduction inside the underlying trigonometric routines.

log — Principal Value and Branch Cut

The complex logarithm is intrinsically multi-valued:

$$\log z = \ln |z| + i(\arg z + 2\pi k), \quad k \in \mathbb{Z}$$

sangi returns the principal value with $k = 0$ and imaginary part in $(-\pi, \pi]$:

$$\operatorname{Log}(z) = \ln |z| + i \arg(z), \quad \arg(z) \in (-\pi, \pi]$$

Because arg follows the atan2 convention, the branch cut lies on the negative real axis ($a < 0$, $b = 0$). Crossing from $b \to 0^+$ gives imaginary part $+\pi$; crossing from $b \to 0^-$ gives $-\pi$.

log10

The common logarithm uses a base change:

$$\log_{10} z = \log z \cdot \log_{10} e$$

For multiprecision types the cached constant T::log10e() avoids recomputing the conversion factor on every call.

sqrt and n-th Roots

sqrt

The principal $\sqrt{z}$ chooses the branch with non-negative real part:

$$\sqrt{z} = \sqrt{|z|} \, e^{i \arg(z) / 2}, \quad \arg(z) / 2 \in (-\pi/2, \pi/2]$$

The branch cut matches log and lies on the negative real axis. For $a < 0$, $\sqrt{z} \to i \sqrt{|a|}$ as $b \to 0^+$ and $\sqrt{z} \to -i \sqrt{|a|}$ as $b \to 0^-$.

The implementation computes $r = \sqrt{|z|}$ and $\varphi = \arg(z) / 2$ and returns polar(r, φ). The real part is automatically non-negative because $\cos\varphi \geq 0$ over the chosen range.

n-th Roots

A complex number has $n$ distinct $n$-th roots:

$$z^{1/n} = r^{1/n} \exp\left(i \frac{\varphi + 2\pi k}{n}\right), \quad k = 0, 1, \ldots, n-1$$

with $r = |z|$, $\varphi = \arg(z)$. The $k = 0$ value is the principal root, and the remaining $n-1$ roots are uniformly spaced on the unit circle. Together the $n$ roots form a regular $n$-gon and carry the cyclic group structure $D_n$.

Typical Uses

  • Enumerating all solutions of $z^n = w$
  • Roots of unity of length $n$ in FFT (a special case $w = 1$, $r = 1$, $\varphi = 0$)
  • Bridging to analogous structures in finite fields and algebraic number fields

Trigonometric Functions

Complex trigonometric functions follow from the real angle-addition formulae:

$$\begin{aligned} \sin(a+bi) &= \sin a \cosh b + i \cos a \sinh b, \\ \cos(a+bi) &= \cos a \cosh b - i \sin a \sinh b, \\ \tan z &= \frac{\sin z}{\cos z}. \end{aligned}$$

When $|b|$ is large, $\cosh b$ and $\sinh b$ may overflow. For floating-point types, the responsibility of branching on $\operatorname{sign}(b)$ to cancel the $e^{|b|}$ factor is delegated to the inner cosh / sinh routines.

$\tan z$ has poles where $\cos z = 0$, i.e. $z = \pi/2 + k\pi$ on the real axis.

Hyperbolic Functions

The hyperbolic functions are obtained analogously:

$$\begin{aligned} \sinh(a+bi) &= \sinh a \cos b + i \cosh a \sin b, \\ \cosh(a+bi) &= \cosh a \cos b + i \sinh a \sin b, \\ \tanh z &= \frac{\sinh z}{\cosh z}. \end{aligned}$$

The trigonometric and hyperbolic functions are related by:

$$\sinh(iz) = i \sin z, \quad \cosh(iz) = \cos z, \quad \tanh(iz) = i \tan z$$

Inverse Trigonometric Functions

The inverse trigonometric functions are realized through closed-form expressions in terms of logarithms. They inherit the branch cuts of sqrt and log:

$$\begin{aligned} \arcsin z &= -i \log\!\left(i z + \sqrt{1 - z^2}\right), \\ \arccos z &= -i \log\!\left(z + i\sqrt{1 - z^2}\right), \\ \arctan z &= \frac{1}{2 i} \log\!\frac{1 + i z}{1 - i z}. \end{aligned}$$

Branch cuts:

  • $\arcsin z$, $\arccos z$ — two real-axis rays with $|x| > 1$
  • $\arctan z$ — two imaginary-axis rays with $|y| > 1$

This matches std::asin / std::acos / std::atan and Python cmath.

Inverse Hyperbolic Functions

$$\begin{aligned} \operatorname{asinh} z &= \log\!\left(z + \sqrt{z^2 + 1}\right), \\ \operatorname{acosh} z &= \log\!\left(z + \sqrt{z^2 - 1}\right), \\ \operatorname{atanh} z &= \frac{1}{2} \log\!\frac{1 + z}{1 - z}. \end{aligned}$$

Branch cuts:

  • $\operatorname{asinh} z$ — imaginary axis with $|y| > 1$
  • $\operatorname{acosh} z$ — real axis with $x < 1$
  • $\operatorname{atanh} z$ — real axis with $|x| > 1$

As with the inverse trigonometric functions, the convention follows std::complex and Python cmath.

Powers

Integer Power pow(z, n)

For integer $n$, sangi uses binary exponentiation (square-and-multiply):

$$z^n = \prod_{k : n_k = 1} z^{2^k}$$

The loop runs over $|n|$ bits and uses $O(\log|n|)$ multiplications. Negative $n$ first inverts $z$ and then proceeds with a positive exponent. Since integer powers have no branch cut, the result is continuous regardless of the branch of $z$.

Complex Power pow(z, w)

For complex $w$, sangi uses the logarithmic route:

$$z^w = e^{w \log z}$$

The branch cut of $\log z$ (the negative real axis) is transferred directly to $z^w$. For instance, $(-1)^{1/2}$ returns $i$ when approached from above the branch cut and $-i$ from below. When $z = 0$, the result is $0$ regardless of $w$ (including $0^0 = 0$).

Real Power pow(z, a)

For real scalar $a$, the same $e^{a \log z}$ path is used. Even if $a$ is integer-valued, it is treated as floating point; for guaranteed integer exponentiation, explicitly call the int overload pow(z, n).

Compatibility with std::complex and cmath

For T = double, every function returns values that are convention-equivalent to std::complex<double> but not necessarily bit-exact. The difference comes from internal implementation choices (Kahan summation, choice of inner sin/cos routine, etc.). This rarely matters in practice but is worth noting when bit-exact tests are required.

Branch cuts, principal-value ranges, and special values (NaN, ±∞) follow the ISO C++ and Python cmath conventions. Typical special values:

InputOutput
log(0)$-\infty + 0i$
log(-1 + 0i)$0 + \pi i$ (upper limit)
sqrt(-1 + 0i)$0 + i$ (upper limit)
pow(0, 0)$0$ (by convention)
exp(NaN + ai)NaN

Design Decisions

Why Return Principal Values

The complex logarithm, complex power, and complex $n$-th root are multi-valued, but a function in a programming language must return a single value. Conventions could differ between implementations, yet ISO C++ std::complex and Python cmath adopt the same principal-value convention. By matching this convention, sangi preserves interoperability and portability.

Accuracy at Multiprecision

For multiprecision types (Float), precision can be configured at runtime. Complex<Float> inherits the precision of the internal Float. Range reduction inside the trigonometric and exponential routines uses cached high-precision constants such as $\pi$ and $\log 2$, so accuracy is preserved at the requested precision.

Separating Integer and Complex Power Paths

The integer power pow(z, n) uses binary exponentiation and avoids any branch cut. The complex power pow(z, w) goes through the logarithm and inherits its branch cut. For common small integer exponents like $n = 2$ or $n = -1$, explicitly choosing the integer overload bypasses branch-cut subtleties and runs faster.

References

  • Kahan, W. (1987). "Branch Cuts for Complex Elementary Functions, or Much Ado about Nothing's Sign Bit". In The State of the Art in Numerical Analysis, Oxford University Press, 165–211.
  • ISO/IEC 14882:2020 (C++20), §26.4 Complex numbers.
  • Python Software Foundation. The Python Standard Library — cmath. (Reference for principal-value and branch conventions.)