Skip to content
C++
Library
since C++98
Intermediate

std::complex

Class template for complex number arithmetic with real and imaginary parts, arithmetic operators, math functions, and C++14 literals.

std::complexsince C++98

A class template in <complex> representing a complex number as a (real, imaginary) pair, providing arithmetic operators, equality comparison, stream I/O, and a complete set of complex-valued mathematical functions.

Overview

std::complex<T> parameterises the scalar type. The standard mandates three explicit specialisations β€” float, double, and long double β€” and since C++11 guarantees binary layout compatibility with C99's _Complex types: a complex<double> occupies exactly two consecutive double values, so reinterpreting to double[2] or passing to a C API expecting double _Complex is well-defined.

The class provides:

  • Full arithmetic: +, -, *, /, compound-assignment forms, and unary negation.
  • Equality comparison: == and !=. No ordering operators exist β€” complex numbers have no natural total order.
  • Stream I/O: operator<< writes (real,imag), operator>> reads the same format.
  • Free-function mathematics: magnitude, argument, squared norm, conjugate, projection, polar construction, and all transcendental functions overloaded for complex arguments.
  • C++14 user-defined literals for concise construction.

Integer and other non-floating-point template arguments are not required to work; implementations may reject them.

Syntax

cpp
#include <complex>

template<typename T>
class std::complex;          // primary template; T should be a floating-point type

std::complex<float>       cf;
std::complex<double>      cd;
std::complex<long double> cl;

Construction methods:

cpp
std::complex<double> a;              // default: (0, 0)
std::complex<double> b(3.0, 4.0);   // (3 + 4i)
std::complex<double> c{3.0, 4.0};   // C++11 brace-init, identical semantics

auto p = std::polar(5.0, 0.9273);   // from magnitude and phase angle (radians)

// C++14 literals β€” requires the using directive
using namespace std::complex_literals;   // C++14
auto d  = 3.0  + 4.0i;    // std::complex<double>
auto df = 3.0f + 4.0if;   // std::complex<float>
auto dl = 3.0l + 4.0il;   // std::complex<long double>

Member access:

cpp
std::complex<double> z(3.0, 4.0);
double r = z.real();   // 3.0
double i = z.imag();   // 4.0
z.real(6.0);           // setter β€” added in C++11
z.imag(8.0);           // setter β€” added in C++11

Examples

Arithmetic and standard math functions

cpp
#include <complex>
#include <iostream>
#include <cmath>

int main() {
    using C = std::complex<double>;

    C z1{3.0, 4.0};
    C z2{1.0, -2.0};

    C sum  = z1 + z2;              // (4, 2)
    C prod = z1 * z2;              // (11, -2)
    C quot = z1 / z2;              // complex division: (z1 * conj(z2)) / norm(z2)

    double mag   = std::abs(z1);   // 5.0       β€” magnitude (L2 norm)
    double phase = std::arg(z1);   // ~0.927 rad β€” phase angle, equivalent to atan2(imag, real)
    double nsq   = std::norm(z1);  // 25.0      β€” SQUARED magnitude: reΒ² + imΒ²
    C      cj    = std::conj(z1);  // (3, -4)   β€” complex conjugate
    C      pr    = std::proj(z1);  // projection onto Riemann sphere β€” C++11

    C root = std::sqrt(C{-1.0, 0.0}); // (0, 1) β€” principal square root
    C ex   = std::exp(C{0.0, M_PI});   // β‰ˆ (-1, Ξ΅) β€” Euler's identity

    std::cout << z1   << '\n';   // (3,4)
    std::cout << prod << '\n';   // (11,-2)
    std::cout << mag  << '\n';   // 5
}

Polynomial evaluation and DFT

cpp
#include <complex>
#include <vector>
#include <cmath>

// Evaluate a real-coefficient polynomial at a complex point β€” Horner's method
std::complex<double> eval_poly(const std::vector<double>& coeffs,
                               std::complex<double> z) {
    std::complex<double> result{};
    for (auto it = coeffs.rbegin(); it != coeffs.rend(); ++it)
        result = result * z + *it;
    return result;
}

// Compute a single DFT bin directly (O(N), for illustration)
std::complex<double> dft_bin(const std::vector<double>& x, std::size_t k) {
    using namespace std::complex_literals;  // C++14
    const std::size_t N = x.size();
    std::complex<double> acc{};
    for (std::size_t n = 0; n < N; ++n) {
        double angle = -2.0 * M_PI * static_cast<double>(k * n) / N;
        acc += x[n] * std::exp(1.0i * angle);
    }
    return acc;
}

Interoperating with C APIs

Since C++11 the standard guarantees that complex<T> has the same binary layout as T[2]:

cpp
#include <complex>
#include <vector>

// Hypothetical C API that takes an array of (re, im) pairs as doubles
extern "C" void fft_inplace(double* data, std::size_t n);

void run_fft(std::vector<std::complex<double>>& buf) {
    // reinterpret_cast here is explicitly permitted by the C++11 standard
    fft_inplace(reinterpret_cast<double*>(buf.data()), buf.size());
}

Best Practices

Choose double by default. std::complex<float> saves memory on large arrays, but precision loss compounds through iterative algorithms. Profile and measure before downgrading.

Use std::polar for phase-domain construction. Writing r * std::cos(theta) and r * std::sin(theta) manually is verbose and slightly less precise; the library version may exploit fused multiply-add or trigonometric co-computation internally.

Compare distances with std::norm, not std::abs. To check whether two complex values are within eps of each other, write std::norm(z1 - z2) < eps * eps. This avoids a square root and is the idiomatic pattern for proximity tests.

Scope complex literals tightly. Put using namespace std::complex_literals; inside a function, not at file scope, to avoid naming conflicts β€” particularly with user-defined or third-party _i literals.

Prefer free-function std::abs over manual hypot. std::abs(z) is numerically safe against intermediate overflow; std::sqrt(z.real()*z.real() + z.imag()*z.imag()) overflows for large components even when the true magnitude is representable.

Common Pitfalls

std::norm is squared magnitude, not the L2 norm. The name is actively misleading. std::norm(z) returns reΒ² + imΒ². To get |z|, use std::abs(z). Confusing these introduces silent numeric errors that only appear at scale.

No std::numeric_limits specialisation. std::numeric_limits<std::complex<double>> is not specialised. It compiles but returns the unspecialised-template defaults β€” typically 0 or false for every member. Query limits through the scalar type: std::numeric_limits<double>::epsilon().

Mixed-precision types do not implicitly convert. std::complex<double> and std::complex<float> are unrelated types. Mixing them in arithmetic is a compile error. Convert explicitly: static_cast<std::complex<double>>(cf).

operator>> format is strict. std::cin >> z expects (3,4) β€” parentheses, a comma, no spaces inside. A bare 3.0 4.0 will not parse correctly; the stream enters a failed state.

i suffix requires the using directive. operator""i, operator""if, and operator""il live in std::literals::complex_literals, not in the global namespace or even in std. A bare using namespace std; does not bring them in. Without using namespace std::complex_literals;, 4.0i is a syntax error.

See Also

  • <cmath> β€” scalar overloads of sin, cos, exp, etc.; <complex> provides separate overloads for complex arguments
  • std::valarray β€” expression-template array type that composes naturally with complex<T> elements for vectorised numerics
  • Operator overloading β€” std::complex is a canonical example of arithmetic operator overloading for user-defined numeric types