Complex — 複素数
概要
Complex<T> は、任意のスカラー型 T に対する複素数クラスである。
T は double, float, Int, Float, Rational など、
ComplexScalar コンセプトを満たす型であれば何でもよい。
- 多倍長対応 —
std::complexとは異なる独自実装で、多倍長数値型もテンプレート引数に取れる - Strassen 3乗算法 — 乗算を 3 回の実数乗算で行い、多倍長演算で高速化を実現する
- Smith 除算 —
T = double/floatではオーバーフローを回避する Smith の方法を使用する - std::complex 互換 — 変換コンストラクタ・暗黙変換演算子を備え、相互変換が可能である
- SIMD 互換レイアウト —
static_assertによりre,imの連続配置を保証する。reinterpret_castでstd::complex<T>と同一メモリとして扱える
ComplexScalar コンセプトは以下の演算を要求する:
template<typename T>
concept ComplexScalar = requires(T a, T b) {
{ a + b } -> std::convertible_to<T>;
{ a - b } -> std::convertible_to<T>;
{ a * b } -> std::convertible_to<T>;
{ a / b } -> std::convertible_to<T>;
{ -a } -> std::convertible_to<T>;
{ T(0) };
{ T(1) };
};
ビルド
#include <math/core/Complex.hpp>
ヘッダオンリーである。CMake やリンクライブラリは不要。
Complex.hpp は <complex>, <cmath>, <concepts> 等の標準ヘッダのみに依存する。
コンストラクタ
| シグネチャ | 説明 |
|---|---|
Complex() |
デフォルトコンストラクタ。実部・虚部ともに T(0) で初期化する。 |
Complex(const T& real) |
実数から構築する。虚部は T(0) になる。暗黙変換を許可する。 |
Complex(const T& real, const T& imag) |
実部と虚部を指定して構築する。 |
explicit Complex(const Complex<U>& other) |
異なるスカラー型 U からの変換。明示的変換 (explicit) が必要である。 |
Complex(const std::complex<U>& sc) |
std::complex からの変換。U が浮動小数点型かつ T に変換可能な場合のみ有効である。 |
アクセサ
| メンバ / メソッド | 説明 |
|---|---|
T re |
実部を直接参照する public メンバ変数。 |
T im |
虚部を直接参照する public メンバ変数。 |
const T& real() const |
実部への const 参照を返す。 |
T& real() |
実部への参照を返す (書き換え可)。 |
void real(const T& val) |
実部を val に設定する。 |
const T& imag() const |
虚部への const 参照を返す。 |
T& imag() |
虚部への参照を返す (書き換え可)。 |
void imag(const T& val) |
虚部を val に設定する。 |
T normSq() const |
$|z|^2 = \mathrm{re}^2 + \mathrm{im}^2$ を返す。 |
std::string toString() const |
文字列表現を返す (例: "3+4i", "-2i")。 |
re / im メンバは public であり、直接アクセスが可能である。
.real() / .imag() メソッドは std::complex との API 互換のために提供している。
算術演算子
Complex 同士の演算
| 演算子 | 説明 |
|---|---|
z + w |
複素数の加算。成分ごとに加算する。 |
z - w |
複素数の減算。成分ごとに減算する。 |
z * w |
複素数の乗算。T が浮動小数点型 (double/float) の場合は通常の 4 乗算 + 2 加減算を使用する。多倍長型 (Int, Float 等) では Strassen 3乗算法 (3 乗算 + 5 加減算) を使用し、乗算コストが支配的な場合に高速化する。 |
z / w |
複素数の除算。T が浮動小数点型の場合は Smith の方法を使用し、中間値のオーバーフローを回避する。多倍長型では素朴な方法 ($\mathrm{denom} = c^2 + d^2$) を使用する。ゼロ除算時は NaN を返す。 |
+z, -z |
単項プラス (恒等)、単項マイナス (符号反転)。 |
スカラー混合演算
Complex<T> と T の間の四則演算をサポートする。左辺・右辺いずれにスカラーが来ても動作する。
| 演算子 | 説明 |
|---|---|
z + s, s + z | 実部にスカラーを加算する。 |
z - s, s - z | 実部からスカラーを減算する / スカラーから複素数を減算する。 |
z * s, s * z | 実部・虚部それぞれにスカラーを乗算する。 |
z / s | 実部・虚部それぞれをスカラーで除算する。 |
s / z | スカラーを複素数で除算する。浮動小数点型では Smith の方法を使用する。 |
複合代入演算子
+=, -=, *=, /= を Complex 同士およびスカラーとの間でサポートする。
比較演算子
| 演算子 | 説明 |
|---|---|
z == w |
実部と虚部がそれぞれ等しい場合に true を返す。浮動小数点型では厳密比較であることに注意すること。 |
z != w |
== の否定を返す。 |
z == s, s == z |
実部がスカラーに等しく、虚部がゼロの場合に true を返す。 |
z != s, s != z |
上記の否定を返す。 |
順序比較 (<, > 等) は複素数では数学的に定義されないため、提供しない。
数学関数
すべて sangi 名前空間の自由関数として提供する。ADL (Argument Dependent Lookup) により、名前空間を明示せずに呼び出せる。
基本関数
| 関数 | シグネチャ | 説明 |
|---|---|---|
abs(z) |
T abs(const Complex<T>&) |
絶対値 $|z| = \sqrt{a^2 + b^2}$ を返す。T が浮動小数点型の場合は std::hypot を使用し、オーバーフローを回避する。 |
arg(z) |
T arg(const Complex<T>&) |
偏角 $\mathrm{arg}(z) = \mathrm{atan2}(b, a)$ を返す。戻り値は $(-\pi, \pi]$ の範囲である。 |
conj(z) |
Complex<T> conj(const Complex<T>&) |
共役複素数 $\bar{z} = a - bi$ を返す。 |
norm(z) |
T norm(const Complex<T>&) |
ノルムの二乗 $|z|^2 = a^2 + b^2$ を返す。STL 互換の名前である (std::norm と同じ意味)。 |
normSq(z) |
T normSq(const Complex<T>&) |
norm(z) と同一。名前がより明示的である。 |
real(z) |
const T& real(const Complex<T>&) |
実部を返す。 |
imag(z) |
const T& imag(const Complex<T>&) |
虚部を返す。 |
proj(z) |
Complex<T> proj(const Complex<T>&) |
リーマン球面への射影を返す。実部または虚部が無限大の場合、$(\infty, \pm 0)$ に写像する。有限の値はそのまま返す。 |
極形式
| 関数 | シグネチャ | 説明 |
|---|---|---|
polar(r, theta) |
Complex<T> polar(const T&, const T&) |
極形式 $r e^{i\theta} = r\cos\theta + ir\sin\theta$ から構築する。 |
expI(omega) |
Complex<T> expI(const T&) |
$e^{i\omega} = \cos\omega + i\sin\omega$ を返す。FFT の回転因子等に便利である。 |
指数・対数・冪乗
| 関数 | シグネチャ | 説明 |
|---|---|---|
exp(z) |
Complex<T> exp(const Complex<T>&) |
$e^z = e^a(\cos b + i\sin b)$ を返す。 |
log(z) |
Complex<T> log(const Complex<T>&) |
自然対数 $\ln z = \ln|z| + i\,\mathrm{arg}(z)$ を返す。主値 (虚部 $\in (-\pi, \pi]$) を返す。 |
log10(z) |
Complex<T> log10(const Complex<T>&) |
常用対数 $\log_{10} z = \ln z \cdot \log_{10} e$ を返す。多倍長型では T::log10e() のキャッシュ済み定数を利用する。 |
sqrt(z) |
Complex<T> sqrt(const Complex<T>&) |
主値の平方根を返す。実部が非負の分枝を選択する。 |
pow(z, n) |
Complex<T> pow(const Complex<T>&, int) |
整数冪 $z^n$ を二進冪乗法で計算する。負の $n$ では逆数を先に求める。 |
pow(z, w) |
Complex<T> pow(const Complex<T>&, const Complex<T>&) |
複素冪 $z^w = e^{w \ln z}$ を返す。$z = 0$ の場合は $0$ を返す。 |
pow(z, a) |
Complex<T> pow(const Complex<T>&, const T&) |
実数冪 $z^a = e^{a \ln z}$ を返す。 |
三角関数
| 関数 | 定義 |
|---|---|
sin(z) | $\sin(a+bi) = \sin a \cosh b + i \cos a \sinh b$ |
cos(z) | $\cos(a+bi) = \cos a \cosh b - i \sin a \sinh b$ |
tan(z) | $\tan z = \sin z / \cos z$ |
双曲線関数
| 関数 | 定義 |
|---|---|
sinh(z) | $\sinh(a+bi) = \sinh a \cos b + i \cosh a \sin b$ |
cosh(z) | $\cosh(a+bi) = \cosh a \cos b + i \sinh a \sin b$ |
tanh(z) | $\tanh z = \sinh z / \cosh z$ |
逆三角関数
| 関数 | 定義 |
|---|---|
asin(z) | $\arcsin z = -i \ln(iz + \sqrt{1 - z^2})$ |
acos(z) | $\arccos z = -i \ln(z + i\sqrt{1 - z^2})$ |
atan(z) | $\arctan z = \frac{1}{2i} \ln\frac{1+iz}{1-iz}$ |
逆双曲線関数
| 関数 | 定義 |
|---|---|
asinh(z) | $\mathrm{asinh}\,z = \ln(z + \sqrt{z^2 + 1})$ |
acosh(z) | $\mathrm{acosh}\,z = \ln(z + \sqrt{z^2 - 1})$ |
atanh(z) | $\mathrm{atanh}\,z = \frac{1}{2}\ln\frac{1+z}{1-z}$ |
ユーザー定義リテラル
using sangi::literals; を宣言すると、以下のリテラルが使用可能になる。
| リテラル | 型 | 例 |
|---|---|---|
_i |
Complex<double> |
3.0_i → $0 + 3i$、3_i → $0 + 3i$ |
_if |
Complex<float> |
3.0_if → Complex<float>(0, 3) |
using sangi::literals;
auto z = 2.0 + 3.0_i; // Complex<double>(2, 3)
auto w = 1.0 - 0.5_i; // Complex<double>(1, -0.5)
auto f = 1.0_if + 2.0_if; // Complex<float>(0, 3)
std::complex との互換性
変換
T が浮動小数点型 (float, double, long double) の場合、以下の変換が暗黙的に行われる。
// std::complex → Complex
std::complex<double> sc(1.0, 2.0);
Complex<double> z = sc; // 暗黙変換
// Complex → std::complex
Complex<double> w(3.0, 4.0);
std::complex<double> sw = w; // 暗黙変換
メモリレイアウト互換
Complex<T> は re, im がメモリ上で連続的に配置されることを static_assert で保証している。
したがって、Complex<double> の配列を std::complex<double> の配列として reinterpret_cast で再解釈できる。
// 配列の相互変換
std::vector<Complex<double>> data(1024);
auto* std_ptr = reinterpret_cast<std::complex<double>*>(data.data());
// FFTW 等の C ライブラリにそのまま渡せる
以下の static_assert により、レイアウトの正しさがコンパイル時に検証される:
sizeof(Complex<T>) == 2 * sizeof(T)offsetof(Complex<T>, re) == 0offsetof(Complex<T>, im) == sizeof(T)
auto と式テンプレート
Complex<T> 自体は式テンプレートを使用しない。
すべての演算子は Complex<T> を直接返すため、auto で受け取っても問題ない。
auto z = a + b * c; // OK: Complex<T> 型
ただし、Matrix<Complex<T>> のように式テンプレートを使用するコンテナの内部で Complex<T> を要素型として使う場合は、
行列演算の結果を auto で受け取ると式テンプレートのプロキシ型になる可能性がある。
その場合は .eval() を呼ぶか、明示的に型を指定すること。
Matrix<Complex<double>> A, B;
auto expr = A * B; // プロキシ型の可能性あり
Matrix<Complex<double>> C = A * B; // 確実に評価される
auto D = (A * B).eval(); // eval() で明示的に評価
コード例
#include <math/core/Complex.hpp>
#include <iostream>
using namespace sangi;
using namespace sangi::literals;
int main() {
// 基本的な構築
Complex<double> z1(3.0, 4.0); // 3 + 4i
Complex<double> z2 = 2.0 + 1.0_i; // 2 + i
// 四則演算
auto sum = z1 + z2; // 5 + 5i
auto prod = z1 * z2; // (3*2 - 4*1) + (3*1 + 4*2)i = 2 + 11i
auto quot = z1 / z2; // Smith の方法で計算
// 数学関数
std::cout << "abs(z1) = " << abs(z1) << std::endl; // 5
std::cout << "arg(z1) = " << arg(z1) << std::endl; // atan2(4,3)
std::cout << "conj(z1) = " << conj(z1) << std::endl; // 3-4i
// 極形式
auto w = polar(5.0, 0.9273); // ≈ 3 + 4i
// 指数・対数
auto e_iz = exp(Complex<double>(0.0, M_PI)); // ≈ -1 + 0i (Euler の公式)
std::cout << "e^(i*pi) = " << e_iz << std::endl;
// std::complex との相互変換
std::complex<double> sc(1.0, 2.0);
Complex<double> from_std = sc;
std::complex<double> to_std = from_std;
// expI: FFT 回転因子
int N = 8;
for (int k = 0; k < N; ++k) {
auto twiddle = expI(-2.0 * M_PI * k / N);
std::cout << "W_" << N << "^" << k << " = " << twiddle << std::endl;
}
}