std.math — Mathematik

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

<WRAP info> 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. </WRAP>

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&#94;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 (#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&#94;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&#94;(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 std.i128 oder std.bignum.
 
Wann eine der neuen Rechen-Units die bessere Wahl ist (→ Mathematik-Units):

^ Anliegen ^ Unit ^
| exp/log/sin mit voller f64-Genauigkeit statt ~1e−6 | std.mathx |
| Bitmengen, Rotation in beliebiger Breite, Schieben ohne Modulo-64 | std.bits |
| Primzahltest jenseits von int64 | std.prime |
| Überlauf melden statt umschlagen lassen | std.sat |
| Fließkomma vergleichen, ULP, Rundungsarten | std.feq |
| Nullstellen, Integration, ODE | std.numeric |

Verwandte Units

  • std.math.constants — Präzisionskonstanten (π, e, √2, φ, ln 2 …), Winkelkonvertierungen, ApproxEqual
  • std.math_batch — SIMD-beschleunigte Batch-Operationen auf int64-Arrays
  • std.vector — 2D/3D-Vektormathematik (baut auf std.math auf)
  • std.stats — Mittelwert, Varianz, Standardabweichung