====== std.math — Mathematik ====== → [[lyx_-_programmiersprache:units|Zurück zur Unit-Übersicht]] Mathematische Grundfunktionen in zwei Schichten: **Ganzzahl-Mathematik** (Betrag, Min/Max, Wurzel, Potenz, Interpolation, Zahlentheorie) und **f64-Mathematik** (Wurzel, Trigonometrie, Exponential- und Logarithmusfunktionen). Die Ganzzahl-Schicht rechnet ohne Fließkomma und ist damit auch auf Zielen ohne FPU einsetzbar; die f64-Schicht setzt auf den Gleitkomma-Builtins ''fAdd''/''fSub''/''fMul''/''fDiv''/''sqrt'' auf. Einsatzbereiche: Geometrie, Skalierung von Messwerten, Animationen, Hashing, Speicherverwaltung (Zweierpotenzen), Statistik-Grundlagen. Basis-Unit für ''std.vector'', ''std.rect'', ''std.circle''. **Autor:** Andreas Röne\\ **Copyright:** 2024–2025 Andreas Röne\\ **Quelle:** ''std/math.lyx'' **Seit lyxc 1.0.20F sind die früher hier verzeichneten Rechenfehler behoben** (#1444–#1450). Nachgemessen: Sin64(30°) = 500000 Hypot64(3,4) = 5 Sin64(90°) = 1000000 IsOdd(-3) = 1 Cos64(60°) = 500000 PopCount(-1) = 64 **Die Winkelfunktionen rechnen in Mikrograd.** ''Sin64(30000000)'' ist der Sinus von 30°, das Ergebnis ist mit 1.000.000 skaliert. Wer die alte Annahme „Grad rein, Promille raus" übernimmt, bekommt für ''Sin64(30)'' eine 0 — das sind 30 Mikrograd, also praktisch null Grad. ===== Import ===== import std.math; ---- ===== Ganzzahl-Funktionen ===== ==== Grundlagen ==== ^ Signatur ^ Beschreibung ^ Status ^ | ''Abs64(x: int64): int64'' | Betrag | ok | | ''Min64(a, b: int64): int64'' | Kleinerer der beiden Werte | ok | | ''Max64(a, b: int64): int64'' | Größerer der beiden Werte | ok | | ''Min3(a, b, c: int64): int64'' | Kleinster von drei Werten | ok | | ''Max3(a, b, c: int64): int64'' | Größter von drei Werten | ok | | ''Sign64(x: int64): int64'' | Vorzeichen: −1, 0 oder 1 | ok | | ''Clamp64(val, min, max: int64): int64'' | Begrenzt ''val'' auf ''[min, max]'' | ok | | ''InRange64(val, min, max: int64): bool'' | Ob ''val'' in ''[min, max]'' liegt (Grenzen eingeschlossen) | ok | | ''Div64(a, b: int64): int64'' | Ganzzahlige Division. **Division durch 0 ist nicht abgefangen** — vorher selbst prüfen | ok | | ''Mod64(a, b: int64): int64'' | Rest der Division; Vorzeichen folgt dem Dividenden | ok | | ''IsEven(x: int64): bool'' | Ob gerade | ok | | ''IsOdd(x: int64): bool'' | Ob ungerade — auch bei negativen Zahlen | **behoben** | | ''TimesTwo(x: int64): int64'' | Verdoppelt — Rest aus der Frühzeit der Unit, ''x * 2'' ist direkter | ok | | ''RoundF64(x: f64): f64'' | Kaufmännisches Runden | ok | ==== Wurzel, Potenz, Logarithmus ==== ^ Signatur ^ Beschreibung ^ Status ^ | ''Sqrt64(n: int64): int64'' | ''floor(√n)'' per Newton-Verfahren; negatives ''n'' ergibt 0 | ok | | ''IntSqrt(n: int64): int64'' | Namensgleicher Aufruf von ''Sqrt64'' | ok | | ''Pow64(base, exp: int64): int64'' | ''base^exp'' durch wiederholte Multiplikation. Negativer Exponent ergibt 0, ''exp == 0'' ergibt 1. **Überlauf wird nicht erkannt** | ok | | ''Log2(x: int64): int64'' | ''floor(log₂ x)'' durch fortgesetzte Halbierung; ''x <= 0'' ergibt 0 | ok | ==== Interpolation und Abbildung ==== ^ Signatur ^ Beschreibung ^ Status ^ | ''Lerp64(a, b, t_permille: int64): int64'' | Lineare Interpolation, ''t'' in **Promille** (0…1000). Werte außerhalb werden geklemmt | ok | | ''Map64(val, in_min, in_max, out_min, out_max: int64): int64'' | Überträgt ''val'' vom Eingangs- in den Ausgangsbereich. Gleiche Grenzen ergeben ''out_min'' | ok | | ''Hypot64(x, y: int64): int64'' | ''√(x²+y²)'' | ok | ==== Zahlentheorie ==== ^ Signatur ^ Beschreibung ^ Status ^ | ''GCD(a, b: int64): int64'' | Größter gemeinsamer Teiler (euklidisch, arbeitet mit Beträgen) | ok | | ''LCM(a, b: int64): int64'' | Kleinstes gemeinsames Vielfaches; 0 wenn ''GCD'' 0 ist | ok | | ''IsPrime(n: int64): bool'' | Probedivision bis ''√n''; ''n < 2'' ergibt ''false'' | ok | | ''NextPrime(n: int64): int64'' | Kleinste Primzahl größer ''n'' | ok | | ''IsPowerOfTwo(x: int64): bool'' | Ob Zweierpotenz (''x & (x−1)''), nur für ''x > 0'' wahr | ok | | ''NextPowerOfTwo(x: int64): int64'' | Kleinste Zweierpotenz ''>= x''; ''x <= 1'' ergibt 1 | ok | | ''PopCount(x: int64): int64'' | Anzahl gesetzter Bits | **behoben** ([[https://github.com/SEOLizer/LyX-Compiler/issues/1545|#1545]]) | ==== Winkel in Mikrograd ==== Diese Gruppe rechnet Winkel als **Mikrograd** (1° = 1 000 000) und Funktionswerte als Festkomma mit Faktor 1 000 000 (1,0 = 1 000 000). ^ Signatur ^ Beschreibung ^ Status ^ | ''Sin64(mikrograd: int64): int64'' | Sinus; Ergebnis ×1.000.000 | ok | | ''Cos64(mikrograd: int64): int64'' | Kosinus; Ergebnis ×1.000.000 | ok | | ''Cos64Inverse(cos_value: int64): int64'' | Arkuskosinus; Ergebnis in Mikrograd | ok | | ''Atan2Microdegrees(y, x: int64): int64'' | Quadranten-korrekter Arkustangens | **behoben** | > **Beide früheren Defekte dieser Seite sind repariert.** Nachgemessen mit 1.0.21A: ''PopCount(-1)'' = 64, ''PopCount(-2)'' = 63; ''Atan2Microdegrees(1, 1)'' = 45 000 000 (also genau 45°), ''(1, 0)'' = 90 000 000, ''(-1, -1)'' = −135 000 000. Ältere Fassungen dieser Seite rieten deshalb zu den f64-Funktionen — das ist nicht mehr nötig. ==== Zufallszahlen ==== ^ Signatur ^ Beschreibung ^ Status ^ | ''RandomRange(max: int64): int64'' | Liefert ''[0, max)'' | **behoben** | | ''RandomBetween(min, max: int64): int64'' | Liefert ''[min, max]'' | **behoben** | > Nachgemessen mit 1.0.21A über je 10 000 Ziehungen: ''RandomRange(100)'' bleibt vollständig in ''[0, 100)'' (min 0, max 99), ''RandomBetween(10, 20)'' verlässt den Bereich kein einziges Mal. Der früher hier verzeichnete Defekt besteht nicht mehr. > > Für **kryptografische** Zwecke ist dieser Generator trotzdem ungeeignet — dafür ''std.crypto.rand''. Die zugrunde liegenden ''Random()'' und ''RandomSeed(n)'' sind Compiler-Builtins, keine Funktionen dieser Unit. ---- ===== f64-Funktionen ===== ==== Grundoperationen ==== ^ Signatur ^ Beschreibung ^ Status ^ | ''AbsF64(x: f64): f64'' | Betrag | ok | | ''NegF64(x: f64): f64'' | Vorzeichenwechsel | ok | | ''MinF64(a, b: f64): f64'' | Kleinerer Wert | ok | | ''MaxF64(a, b: f64): f64'' | Größerer Wert | ok | | ''FloorF64(x: f64): f64'' | Abrunden | ok | | ''CeilF64(x: f64): f64'' | Aufrunden | ok | | ''RoundF64(x: f64): f64'' | Kaufmännisch runden | ok | | ''TruncF64(x: f64): f64'' | Nachkommastellen abschneiden (Richtung Null) | ok | | ''SqrtF64(x: f64): f64'' | Quadratwurzel (nutzt das ''sqrt''-Builtin) | ok | ==== Trigonometrie ==== Alle Winkel im **Bogenmaß**. ^ Signatur ^ Beschreibung ^ Genauigkeit ^ | ''SinF64(x: f64): f64'' | Sinus (Taylor-Reihe nach Argumentreduktion) | ~1e−6 über den geprüften Bereich | | ''CosF64(x: f64): f64'' | Kosinus über ''sin(x + π/2)'' | wie ''SinF64'' | | ''TanF64(x: f64): f64'' | Tangens als Quotient; ''cos(x) == 0'' ergibt 0 | ~1e−6 | | ''AtanF64(x: f64): f64'' | Arkustangens, zweistufige Argumentreduktion + 13-gliedrige Reihe | besser als 1e−6 (gemessen bis zur Anzeigegrenze) | | ''Atan2F64(y, x: f64): f64'' | Quadranten-korrekt, Bereich −π…π | wie ''AtanF64'' | | ''AsinF64(x: f64): f64'' | Arkussinus über ''atan(x/√(1−x²))'' | besser als 1e−6 | | ''AcosF64(x: f64): f64'' | Arkuskosinus über ''π/2 − asin(x)'' | besser als 1e−6 | ==== Exponential und Logarithmus ==== ^ Signatur ^ Beschreibung ^ Genauigkeit ^ | ''ExpF64(x: f64): f64'' | ''e^x'' | ~1e−6 | | ''LogF64(x: f64): f64'' | Natürlicher Logarithmus; ''x <= 0'' liefert −1.0 als Fehlerkennung | ~1e−6 | | ''Log10F64(x: f64): f64'' | ''ln(x)/ln(10)'' | ~1e−6 | | ''Log2F64(x: f64): f64'' | Logarithmus zur Basis 2 | ~1e−6 | | ''PowF64(base, exp: f64): f64'' | ''e^(exp · ln base)'' | ~1e−6 | Seit dem Fix von #1449 sind alle vier über den ganzen Wertebereich brauchbar — geprüft: ''ExpF64(5)'' = 148.413159, ''LogF64(100)'' = 4.605170, ''PowF64(2, 10)'' = 1024.000000. Die verbleibende Abweichung liegt bei rund einer Millionstel. Zu beachten bleibt: ''LogF64(x <= 0)'' gibt ''−1.0'' als Fehlerkennung zurück — das ist zugleich ein gültiges Ergebnis (''ln(1/e) = −1''), Fehler und Resultat sind also nicht unterscheidbar. ---- ===== Beispiele ===== ==== Ganzzahl-Grundlagen ==== import std.math; fn main(): int64 { // Ganzzahl-Grundlagen PrintLn("Abs64(-42) = ", IntToStr(Abs64(-42))); PrintLn("Sign64(-7) = ", IntToStr(Sign64(-7))); PrintLn("Clamp64(150,0,100)= ", IntToStr(Clamp64(150, 0, 100))); PrintLn("InRange64(50,0,100)= ", IntToStr(InRange64(50, 0, 100) as int64)); PrintLn("Min3(9,4,7) = ", IntToStr(Min3(9, 4, 7))); PrintLn("Max3(9,4,7) = ", IntToStr(Max3(9, 4, 7))); // Wurzel, Potenz, Logarithmus PrintLn("Sqrt64(1000000) = ", IntToStr(Sqrt64(1000000))); PrintLn("Sqrt64(10) = ", IntToStr(Sqrt64(10)), " (floor)"); PrintLn("Pow64(2,10) = ", IntToStr(Pow64(2, 10))); PrintLn("Log2(1024) = ", IntToStr(Log2(1024))); PrintLn("Log2(1000) = ", IntToStr(Log2(1000)), " (floor)"); // Skalierung PrintLn("Lerp64(100,200,500) = ", IntToStr(Lerp64(100, 200, 500))); PrintLn("Map64(512,0,1023,0,100) = ", IntToStr(Map64(512, 0, 1023, 0, 100))); return 0; } Ausgabe: Abs64(-42) = 42 Sign64(-7) = -1 Clamp64(150,0,100)= 100 InRange64(50,0,100)= 1 Min3(9,4,7) = 4 Max3(9,4,7) = 9 Sqrt64(1000000) = 1000 Sqrt64(10) = 3 (floor) Pow64(2,10) = 1024 Log2(1024) = 10 Log2(1000) = 9 (floor) Lerp64(100,200,500) = 150 Map64(512,0,1023,0,100) = 50 ''Lerp64'' interpoliert in Promille: ''t = 500'' sind 50 %, daher genau die Mitte zwischen 100 und 200. ''Map64'' skaliert einen 10-Bit-Sensorwert (0…1023) auf Prozent. ==== Zahlentheorie ==== import std.math; fn main(): int64 { PrintLn("GCD(48,18) = ", IntToStr(GCD(48, 18))); PrintLn("LCM(4,6) = ", IntToStr(LCM(4, 6))); PrintLn("IsPrime(97) = ", IntToStr(IsPrime(97) as int64)); PrintLn("NextPrime(90) = ", IntToStr(NextPrime(90))); PrintLn("IsPowerOfTwo(1024) = ", IntToStr(IsPowerOfTwo(1024) as int64)); PrintLn("NextPowerOfTwo(1000)= ", IntToStr(NextPowerOfTwo(1000))); PrintLn("PopCount(255) = ", IntToStr(PopCount(255))); // Primzahlen bis 30 sammeln var n: int64 := 2; var zeile: pchar := "Primzahlen < 30:"; while (n < 30) { if (IsPrime(n)) { zeile := StrConcat(zeile, StrConcat(" ", IntToStr(n))); } n := n + 1; } PrintLn(zeile); return 0; } Ausgabe: GCD(48,18) = 6 LCM(4,6) = 12 IsPrime(97) = 1 NextPrime(90) = 97 IsPowerOfTwo(1024) = 1 NextPowerOfTwo(1000)= 1024 PopCount(255) = 8 Primzahlen < 30: 2 3 5 7 11 13 17 19 23 29 ''NextPowerOfTwo'' ist der übliche Weg, eine Puffergröße auf die nächste Zweierpotenz aufzurunden. ==== f64-Mathematik ==== import std.io; import std.math; fn zeige(name: pchar, v: f64): void { PrintLn(name, " = ", FloatToStr(v, 4)); // zweites Argument: Nachkommastellen } fn main(): int64 { zeige("SqrtF64(2) ", SqrtF64(2 as f64)); zeige("SinF64(1) ", SinF64(1 as f64)); zeige("CosF64(0) ", CosF64(0 as f64)); zeige("AtanF64(1) ", AtanF64(1 as f64)); zeige("Atan2F64(1,1)", Atan2F64(1 as f64, 1 as f64)); zeige("AcosF64(0) ", AcosF64(0 as f64)); zeige("Log2F64(1024)", Log2F64(1024 as f64)); zeige("ExpF64(1) ", ExpF64(1 as f64)); // Runden und Begrenzen var x: f64 := fDiv(7 as f64, 2 as f64); // 3.5 zeige("FloorF64(3.5)", FloorF64(x)); zeige("CeilF64(3.5) ", CeilF64(x)); zeige("RoundF64(3.5)", RoundF64(x)); zeige("TruncF64(3.5)", TruncF64(x)); zeige("AbsF64(-3.5) ", AbsF64(fNeg(x))); zeige("MinF64(3.5,2)", MinF64(x, 2 as f64)); return 0; } Ausgabe: SqrtF64(2) = 1.414213 SinF64(1) = 0.841470 CosF64(0) = 1.000000 AtanF64(1) = 0.785398 Atan2F64(1,1) = 0.785398 AcosF64(0) = 1.570796 Log2F64(1024) = 10.000000 ExpF64(1) = 2.718281 FloorF64(3.5) = 3.000000 CeilF64(3.5) = 4.000000 RoundF64(3.5) = 4.000000 TruncF64(3.5) = 3.000000 AbsF64(-3.5) = 3.500000 MinF64(3.5,2) = 2.000000 Die Genauigkeit reicht heute bis an die Anzeigegrenze von sechs Nachkommastellen: ''CosF64(0)'' liefert 1.000000, ''SinF64(3)'' das korrekte 0.141120. Bis Version 1.0.12A rechneten Sinus und Kosinus mit der Näherung π ≈ 355/113 und wichen ab der sechsten Stelle ab — diese Einschränkung gilt nicht mehr. Gleitkommazahlen entstehen in Lyx über ''as f64'' oder ''fDiv'' — ein Literal wie ''3.5'' gibt es in dieser Form nicht, daher ''fDiv(7 as f64, 2 as f64)''. ---- ===== Genauigkeit und bekannte Fehler ===== Alle Angaben mit **lyxc 1.0.20F** gemessen. Die früher hier verzeichneten Rechenfehler sind behoben (#1444–#1450): ^ Aufruf ^ Ergebnis ^ erwartet ^ | ''Sin64(30000000)'' | 500000 | 500000 | | ''Sin64(90000000)'' | 1000000 | 1000000 | | ''Cos64(60000000)'' | 500000 | 500000 | | ''Cos64(0)'' | 1000000 | 1000000 | | ''Hypot64(3, 4)'' | 5 | 5 | | ''Atan2Microdegrees(1000, 1000)'' | 45000000 | 45000000 (= 45°) | | ''IsOdd(-3)'' | 1 | 1 | | ''PopCount(-1)'' | 64 | 64 | | ''RoundF64(2.6)'' | 3.000000 | 3.000000 | ==== Winkel werden in Mikrograd übergeben ==== Das ist die wichtigste Umstellung: ''Sin64'', ''Cos64'' und ''Atan2Microdegrees'' rechnen in **Mikrograd**, das Ergebnis der Winkelfunktionen ist mit 1.000.000 skaliert. Sin64(30000000) // Sinus von 30° → 500000 (= 0,5) Cos64(0) // → 1000000 (= 1,0) Wer versehentlich Grad einsetzt, bekommt kein falsches Ergebnis, sondern praktisch null: ''Sin64(30)'' sind 30 Mikrograd, also 0,00003° — der Sinus davon ist auf sechs Stellen 0. Der Fehler fällt damit leicht als „Funktion kaputt" fehlgedeutet aus. ==== Round64 heißt jetzt RoundF64 ==== Die frühere Attrappe ''Round64(x: int64)'' ist entfallen (#1450). An ihrer Stelle steht ''RoundF64(x: f64): f64'', das tatsächlich rundet. ==== Exp, Log und Pow arbeiten jetzt genau (#1449 behoben) ==== ^ Aufruf ^ Ergebnis ^ korrekt ^ | ''ExpF64(1)'' | 2.718281 | 2.718282 | | ''ExpF64(3)'' | 20.085536 | 20.085537 | | ''ExpF64(5)'' | 148.413159 | 148.413159 | | ''LogF64(10)'' | 2.302585 | 2.302585 | | ''LogF64(100)'' | 4.605170 | 4.605170 | | ''Log10F64(100)'' | 1.999999 | 2.000000 | | ''PowF64(2, 10)'' | 1024.000000 | 1024.000000 | Früher fehlte die Argumentreduktion, wodurch ''ExpF64(5)'' um 13 % und ''PowF64(2, 10)'' um 39 % danebenlagen. Das ist behoben; die verbleibende Abweichung von etwa 1e−6 stammt aus der Festkomma-Näherung von π und e und reicht für alles außer numerisch empfindlichen Rechnungen. ==== ''Round64(x: int64): int64'' gibt ''x'' unverändert zurück. Da der Parameter bereits ganzzahlig ist, gibt es nichts zu runden — die Funktion kann nur aus einem abgebrochenen Umbau stammen. Sie sieht am Aufrufort nach einer Rundung aus und tut nichts. Für f64 ''RoundF64'' verwenden. ---- ===== Empfehlung ===== **Die Warnungen früherer Fassungen dieser Seite sind hinfällig.** Gegen 1.0.21A einzeln nachgemessen: ''PopCount(-1)'' = 64, ''IsOdd(-3)'' = 1, ''Hypot64(3, 4)'' = 5 und ''Hypot64(300, 400)'' = 500, ''Sin64(45°)'' = 707107, ''Cos64(60°)'' = 500000, ''Atan2Microdegrees(1, 1)'' = 45 000 000, und beide Zufallsfunktionen halten über 10 000 Ziehungen ihren Bereich ein. * Ganzzahl-Grundlagen und Zahlentheorie sind belastbar, **einschließlich** ''PopCount'' und ''IsOdd'' bei negativen Argumenten. * Die Mikrograd-Gruppe ist benutzbar. Zu beachten bleibt allein die **Einheit**: Winkel gehen in Mikrograd hinein (1° = 1 000 000), Funktionswerte kommen mit Faktor 1 000 000 heraus. * ''Pow64'' läuft bei großen Werten still über; Argumente vorab gegen ''Log2'' plausibilisieren. Wer den vollen Bereich braucht, nimmt [[lyx_-_programmiersprache:units:i128|std.i128]] oder [[lyx_-_programmiersprache:units:bignum|std.bignum]]. > **Wann eine der neuen Rechen-Units die bessere Wahl ist** (→ [[lyx_-_programmiersprache:units:mathematik|Mathematik-Units]]): > > ^ Anliegen ^ Unit ^ > | ''exp''/''log''/''sin'' mit voller ''f64''-Genauigkeit statt ~1e−6 | [[lyx_-_programmiersprache:units:mathx|std.mathx]] | > | Bitmengen, Rotation in beliebiger Breite, Schieben ohne Modulo-64 | [[lyx_-_programmiersprache:units:bits|std.bits]] | > | Primzahltest jenseits von ''int64'' | [[lyx_-_programmiersprache:units:prime|std.prime]] | > | Überlauf melden statt umschlagen lassen | [[lyx_-_programmiersprache:units:sat|std.sat]] | > | Fließkomma vergleichen, ULP, Rundungsarten | [[lyx_-_programmiersprache:units:feq|std.feq]] | > | Nullstellen, Integration, ODE | [[lyx_-_programmiersprache:units:numeric|std.numeric]] | ---- ===== Verwandte Units ===== * [[lyx_-_programmiersprache:units:math:constants|std.math.constants]] — Präzisionskonstanten (π, e, √2, φ, ln 2 …), Winkelkonvertierungen, ''ApproxEqual'' * [[lyx_-_programmiersprache:units:math_batch|std.math_batch]] — SIMD-beschleunigte Batch-Operationen auf int64-Arrays * [[lyx_-_programmiersprache:units:vector|std.vector]] — 2D/3D-Vektormathematik (baut auf std.math auf) * [[lyx_-_programmiersprache:units:stats|std.stats]] — Mittelwert, Varianz, Standardabweichung