====== std.complex — komplexe Zahlen ====== Die komplexe Zahl als **Wert**: Division nach Smith, Betrag über ''hypot'' (überlaufsicher bis 1e200), Verzweigungsschnitte nach C99 Annex G. → [[lyx_-_programmiersprache:units:mathematik|Mathematik-Units]] · [[lyx_-_programmiersprache:units:fft|std.fft]] · [[lyx_-_programmiersprache:units:tensor|std.tensor]] · [[lyx_-_programmiersprache:units:mathx|std.mathx]] import std.complex; Schicht 2, hängt an [[lyx_-_programmiersprache:units:dist|std.dist]], [[lyx_-_programmiersprache:units:feq|std.feq]] und [[lyx_-_programmiersprache:units:mathx|std.mathx]]. 56 Funktionen. Alle Beispiele mit ''lyxc 1.0.21A'' übersetzt und ausgeführt. ---- > **Die Struct-Rückgabe legt keinen Speicher mehr an** ([[https://github.com/SEOLizer/LyX-Compiler/issues/1580|#1580]] behoben). Vorher kostete jeder Aufruf zwei ''mmap'' ohne ''munmap'' — die Kopie bei der Rückgabe und, größer noch, die Deklaration ''%%var p: Pair;%%'' mit 4096 Byte. Nachgemessen mit 1.1.2E über 200 000 Rückgaben: > > Laufzeit 0,05 s, Spitzenspeicher 6272 kB mmap-Aufrufe insgesamt: 9 (Programmstart, nicht die Schleife) > > Der frühere Rat, in Schleifen adressbasiert zu arbeiten, ist damit hinfällig. Für Signalverarbeitung über viele Werte bleibt [[lyx_-_programmiersprache:units:fft|std.fft]] mit getrennten Re/Im-Feldern trotzdem der direktere Weg; ''CxLoad''/''CxStore'' schlagen die Brücke. ---- ===== Zwei Darstellungen, eine Brücke ===== ''std.fft'' und ''std.tensor'' halten Real- und Imaginärteil in **getrennten Feldern** — gut für lange Reihen, umständlich für eine einzelne Zahl. ''std.complex'' ergänzt die einzelne Zahl als Wert: var z: Cx := CxLoad(reArr, imArr, i); // aus getrennten Feldern CxStore(z, reArr, imArr, i); // und zurück Damit lässt sich eine einzelne Stelle bequem rechnen, ohne die Feldstruktur aufzugeben. ---- ===== Rechnen ===== unit main; import std.io; import std.string; import std.complex; fn ZC(t: pchar, z: Cx): void { Print(t); PrintF64(CxRe(z)); Print(" im="); PrintF64(CxIm(z)); } fn ZF(t: pchar, v: f64): void { Print(t); PrintF64(v); } fn main(): int64 { var a: Cx := CxNew(3.0, 4.0); var b: Cx := CxNew(1.0, 2.0); ZF("Abs(3+4i) re=", CxAbs(a)); ZC("Mul(3+4i, 1+2i) re=", CxMul(a, b)); ZC("Div(3+4i, 1+2i) re=", CxDiv(a, b)); ZF("Arg(i) re=", CxArg(CxI())); var big: Cx := CxNew(1.0e200, 1.0e200); ZF("Abs(1e200+1e200i)/1e200 = ", CxAbs(big) / 1.0e200); return 0; } Abs(3+4i) re=5.000000 Mul(3+4i, 1+2i) re=-5.000000 im=10.000000 Div(3+4i, 1+2i) re=2.200000 im=-0.400000 Arg(i) re=1.570796 Abs(1e200+1e200i)/1e200 = 1.414213 Sämtliche Werte stimmen mit der Referenzrechnung überein. ---- ===== Zwei Entwurfsentscheidungen, die zählen ===== > **Der Betrag läuft über ''hypot'', nicht über ''√(re² + im²)''.** Bei ''1e200 + 1e200i'' überläuft schon das Quadrat — die naive Formel liefert ''inf'', obwohl das Ergebnis ''1,41e200'' problemlos darstellbar ist. Oben ist es nachgemessen: geteilt durch ''1e200'' kommt ''1,414213'' heraus, also ein endlicher Wert. > > **Die Division rechnet nach Smith**, nicht nach der Lehrbuchformel. ''(ac+bd)/(c²+d²)'' überläuft aus demselben Grund. Smith klammert vorher den betragsgrößeren Nenneranteil aus und bleibt damit im darstellbaren Bereich. ---- ===== Verzweigungsschnitte ===== Bei ''sqrt'', ''log'', ''asin'', ''acos'', ''atanh'' und Verwandten ist die komplexe Funktion mehrdeutig; welcher Zweig gilt, legt eine Konvention fest. ''std.complex'' folgt **C99 Annex G** — derselben Festlegung wie die C-Standardbibliothek und Pythons ''cmath''. > **Das Vorzeichen der Null entscheidet mit.** Auf dem Schnitt selbst — etwa bei ''log'' entlang der negativen reellen Achse — hängt es von ''−0,0'' gegen ''+0,0'' ab, ob das Ergebnis ''+πi'' oder ''−πi'' lautet. Genau hier ist ''Atan2F64'' aus ''std.math'' fehlerhaft ([[https://github.com/SEOLizer/LyX-Compiler/issues/1577|#1577]]): es verwirft das Vorzeichen der Null und landet in der falschen Halbebene. ''std.complex'' rechnet deshalb über ''MxAtan2'' aus [[lyx_-_programmiersprache:units:mathx|std.mathx]]. ---- ===== Funktionsübersicht ===== ^ Gruppe ^ Funktionen ^ | Erzeugen | ''CxNew'' · ''CxFromReal'' · ''CxFromImag'' · ''CxFromPolar'' · ''CxZero'' · ''CxOne'' · ''CxI'' · ''CxCopy'' | | Zerlegen | ''CxRe'' · ''CxIm'' · ''CxAbs'' · ''CxAbs2'' · ''CxArg'' | | Prüfen | ''CxIsZero'' · ''CxIsReal'' · ''CxIsNaN'' · ''CxIsInf'' · ''CxIsFinite'' | | Grundrechenarten | ''CxAdd'' · ''CxSub'' · ''CxMul'' · ''CxDiv'' · ''CxNeg'' · ''CxConj'' · ''CxInv'' | | Mit einer reellen Zahl | ''CxAddReal'' · ''CxSubReal'' · ''CxMulReal'' · ''CxDivReal'' | | Brücke zu Feldern | ''CxLoad'' · ''CxStore'' | Dazu die analytischen Funktionen — Wurzel, Exponential, Logarithmus, Potenz, Winkel- und Hyperbelfunktionen samt Umkehrungen — durchgehend mit den Schnitten nach C99. ''CxAbs2'' spart die Wurzel: für **Vergleiche** genügt das Betragsquadrat, und es ist zugleich der Ausdruck, der in Energie- und Leistungsrechnungen ohnehin gebraucht wird. > **Bei einer Abweichung zuerst die Referenz prüfen.** Beim komplexen ''pow'' wich das Ergebnis um 8e−6 von Pythons ''a**b'' ab. Mit einer Rechnung auf 60 Stellen nachgeprüft lag **diese Unit** bei 1,1e−13 und **Python** bei 8,3e−6. Ist eine Abweichung zu groß für IEEE-Rundung, muss nicht der eigene Code falsch sein. ---- Letzte Aktualisierung: 2026-08-16 · alle Beispiele mit ''lyxc 1.0.21A'' übersetzt und ausgeführt; Betrag, Produkt, Quotient und die Überlaufsicherheit gegen eine unabhängige Referenzrechnung geprüft, #1580 selbst nachgemessen.