Go gehört zu den wenigen Mainstream-Sprachen, die komplexe Zahlen direkt in die Sprache eingebaut haben — als eigene Typen complex64 und complex128, mit imaginärem Literal-Suffix i und drei Built-in-Funktionen für Konstruktion und Zerlegung. Im Backend- und Web-Alltag braucht man das fast nie. Wer aber Signal-Processing, FFT, Physik-Simulationen oder geometrische Transformationen schreibt, bekommt die übliche (re, im)-Mathematik ohne Library geliefert. Dieser Artikel zeigt die beiden Typen, die Literal-Syntax, das math/cmplx-Paket und wo die typischen Stolperfallen liegen.
Was Complex in Go bietet
Go kennt genau zwei eingebaute Complex-Typen:
| Typ | Real-Anteil | Imaginär-Anteil | Gesamtgröße |
|---|---|---|---|
complex64 | float32 | float32 | 64 Bit |
complex128 | float64 | float64 | 128 Bit |
Beide Typen sind predeclared — du brauchst keinen Import, um sie zu nutzen. Die Komponenten sind ganz normale IEEE-754-Floats, mit allen üblichen Eigenheiten (siehe Float-Artikel). complex128 ist die idiomatische Default-Wahl; complex64 nimmt man nur, wenn Speicher knapp ist (große Arrays, GPU-nahe Daten).
Imaginäres Literal mit i-Suffix
Eine imaginäre Konstante ist eine Integer- oder Float-Zahl gefolgt vom kleingeschriebenen Buchstaben i:
package main
import "fmt"
func main() {
a := 1 + 2i // complex128: real=1, imag=2
b := 3.14i // complex128: real=0, imag=3.14
c := 0i // complex128: 0+0i
d := 6.67428e-11i // wissenschaftliche Notation erlaubt
fmt.Printf("%v %v %v %v\n", a, b, c, d)
}Der Compiler erkennt aus 1 + 2i automatisch den Complex-Typ. Imaginäre Literale folgen denselben Regeln wie Float-Literale — Hex (0x1p-2i), wissenschaftliche Notation (1E6i) und Underscore-Trennzeichen sind alle erlaubt. Eine Eigenheit zur Rückwärtskompatibilität: 0123i wird als dezimales 123i gelesen, nicht als Oktal. Wer Oktal will, muss explizit 0o123i schreiben.
Built-ins: complex, real, imag
Drei Built-in-Funktionen reichen für Konstruktion und Zerlegung — kein Import nötig:
package main
import "fmt"
func main() {
c := complex(3, 4) // 3+4i als complex128
r := real(c) // 3 (float64)
i := imag(c) // 4 (float64)
fmt.Printf("c=%v real=%v imag=%v\n", c, r, i)
// complex64 mit float32-Argumenten:
var f32a, f32b float32 = 1.5, 2.5
small := complex(f32a, f32b) // ergibt complex64
fmt.Printf("%T\n", small)
}c=(3+4i) real=3 imag=4
complex64complex(re, im) wählt den Ergebnistyp anhand der Argument-Typen: float32 → complex64, float64 → complex128. real() und imag() geben den passenden Float-Typ zurück.
Arithmetik mit Complex
Die vier Grundrechenarten +, -, *, / funktionieren direkt am Complex-Typ — implementiert nach den üblichen Regeln der komplexen Arithmetik:
package main
import "fmt"
func main() {
a := 2 + 3i
b := 1 - 1i
fmt.Println(a + b) // (3+2i)
fmt.Println(a - b) // (1+4i)
fmt.Println(a * b) // (5+1i) — (2+3i)(1-1i) = 2-2i+3i-3i² = 5+i
fmt.Println(a / b) // (-0.5+2.5i)
}(3+2i)
(1+4i)
(5+1i)
(-0.5+2.5i)Modulo (%) gibt es nicht — definiert ist es für komplexe Zahlen ohnehin nicht. Vergleich mit < oder > ebenfalls nicht (komplexe Zahlen sind nicht total geordnet); nur == und != sind erlaubt.
Das math/cmplx-Paket
Was über die vier Grundrechenarten hinausgeht, lebt im Standard-Paket math/cmplx. Alle Funktionen arbeiten ausschließlich mit complex128:
package main
import (
"fmt"
"math/cmplx"
)
func main() {
c := 3 + 4i
fmt.Println(cmplx.Abs(c)) // 5 (Betrag/Modul)
fmt.Println(cmplx.Phase(c)) // 0.927… (Argument in Radiant)
r, θ := cmplx.Polar(c) // Polarkoordinaten
fmt.Printf("r=%v θ=%v\n", r, θ)
fmt.Println(cmplx.Sqrt(-1)) // (0+1i)
fmt.Println(cmplx.Exp(1i * 3.14159265)) // ≈ -1+0i (Eulers Identität)
fmt.Println(cmplx.Conj(c)) // 3-4i
}5
0.9272952180016122
r=5 θ=0.9272952180016122
(0+1i)
(-1+1.2246467991473515e-16i)
(3-4i)Weiterhin verfügbar: Log, Log10, Pow, Rect (von Polar zurück), trigonometrische Funktionen Sin/Cos/Tan samt Inversen, hyperbolische Varianten sowie Inf, NaN, IsInf, IsNaN für Spezialwerte. Das Paket folgt C99 Annex G, der Norm-Vorgabe für komplexe Arithmetik.
Wann braucht man Complex überhaupt?
Ehrlich gesagt: in typischen Web- und Backend-Workloads praktisch nie. Die natürlichen Anwendungsfelder liegen anderswo:
- Signalverarbeitung — DFT/FFT, Filter-Design, Audio- und Bildverarbeitung. Frequenzbereich ist immer komplex.
- Physik-Simulation — Quantenmechanik (Wellenfunktionen sind komplexwertig), AC-Stromkreise (Impedanz), Wellengleichungen.
- Geometrische Transformationen — Rotation in der Ebene als Multiplikation mit
e^(iθ)ist eleganter als 2x2-Matrizen. - Kontrolltechnik — Pol-Nullstellen-Analyse, Bode-Plots, Stabilitätsanalyse.
- Numerische Mathematik — Wurzeln von Polynomen höheren Grades sind oft komplex, auch wenn die Koeffizienten reell sind.
Für eine REST-API, einen Scheduler oder ein CLI-Tool wirst du complex128 nie sehen. Das Sprachfeature ist trotzdem da — eine bewusste Entscheidung der Go-Designer, die Numerik-Modell aus C übernommen haben.
FAQ
Wieso hat Go eingebauten Complex-Support, aber kein Decimal?
Go orientiert sich am C-/IEEE-754-Numerik-Modell. Komplexe Zahlen sind dort als _Complex seit C99 Sprach-Feature, also hat Go sie übernommen. Decimal-Arithmetik (wie Pythons decimal oder Javas BigDecimal) gehört nicht zur IEEE-Hardware-Welt und lebt in Go ausschließlich in Drittpaketen — etwa shopspring/decimal. Eine Konsistenz-Entscheidung, keine grundsätzliche Bevorzugung.
Welcher Complex-Typ ist Default?
Bei untypisierten Literalen wie `1 + 2i` wählt Go `complex128`. Das spiegelt die Default-Wahl bei Floats wider (`float64` ist Standard). `complex64` entsteht nur, wenn du explizit mit `float32`-Argumenten konstruiert oder den Typ explizit angibst.
Kann man Complex mit anderen Numerischen mischen?
Nein. Wie überall in Go braucht es explizite Konvertierung. `var x float64 = 3.0; c := complex(x, 0)` ist der saubere Weg, ein Float in einen Complex zu heben. Ein direktes `x + 1i` mit typisiertem x schlägt fehl, weil Typen nicht gemischt werden.
Vergleich mit "==" möglich?
Ja — `==` und `!=` sind definiert und vergleichen Real- und Imaginärteil paarweise. Die üblichen Floating-Point-Caveats gelten unverändert: rechnest du Werte ineinander um, sind exakte Gleichheits-Checks selten verlässlich. Besser über `cmplx.Abs(a - b) < epsilon` arbeiten. Ordnungsvergleiche (`<`, `>`) gibt es nicht, da komplexe Zahlen keine totale Ordnung haben.
Format-Verb in fmt.Printf?
Es gibt kein eigenes Verb wie `%c`. Stattdessen funktioniert `%v` universell und liefert die Form `(re+imi)`. Auch `%g` und `%f` sind anwendbar und formatieren beide Komponenten konsistent. (`%c` ist in Go für Unicode-Zeichen reserviert — siehe Rune.)
Wie konvertiere ich complex64 nach complex128?
Wie jede Type Conversion in Go: `big := complex128(c)`. Das hebt Real- und Imaginärteil von `float32` auf `float64` hoch — verlustfrei. Andersherum (`complex64(big)`) ist es eine Verkürzung mit potenziellem Genauigkeitsverlust, genau wie bei den Floats darunter.
Weiterführende Ressourcen
Externe Quellen
- Numeric types – Go Specification
- Imaginary literals – Go Specification
- Paket math/cmplx – pkg.go.dev