====== 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