std.rat — exakte Brüche
Rationale Zahlen auf zwei int64. Jeder Überlauf wird gemeldet statt abgeschnitten; beste Näherung mit Semikonvergenten.
→ Mathematik-Units · std.i128 · std.money · std.feq
import std.rat;
Schicht 2, hängt an std.i128 und std.math. 42 Funktionen. Alle Beispiele mit lyxc 1.0.21A übersetzt und ausgeführt.
Die Struct-Rückgabe legt keinen Speicher mehr an (#1580 behoben). Vorher kostete jeder Aufruf zweimmapohnemunmap: die Kopie bei der Rückgabe und, größer noch, die Deklarationvar r: Rat;mit 4096 Byte. Nachgemessen mit 1.1.2E über 200 000 Rückgaben:
^ ^ vorher (1.0.21A) ^ jetzt (1.1.2E) ^
|mmap-Aufrufe | 200 000 | 9 (nur Programmstart) |
| Speicher | 800 MB | 6,3 MB |
| Laufzeit | 3,53 s | 0,05 s |
Die Unit ist damit auch in Schleifen brauchbar; das gilt ebenso für std.complex und std.quat.
Exakt heißt exakt
unit main;
import std.io;
import std.string;
import std.alloc;
import std.rat;
fn ZI(t: pchar, v: int64): void { Print(t); PrintLn(IntToStr(v)); }
fn ZR(t: pchar, a: Rat): void {
Print(t); Print(IntToStr(RatNum(a))); Print("/"); PrintLn(IntToStr(RatDen(a)));
}
fn main(): int64 {
var a: Rat := RatNew(1, 3);
var b: Rat := RatNew(1, 6);
ZR("1/3 + 1/6 = ", RatAdd(a, b));
ZR("1/3 * 1/6 = ", RatMul(a, b));
ZR("1/3 / 1/6 = ", RatDiv(a, b));
ZR("6/8 gekuerzt = ", RatNew(6, 8));
ZR("(1/3)^3 = ", RatPow(a, 3));
var big: Rat := RatNew(9223372036854775807, 2);
var sum: Rat := RatAdd(big, big);
ZI("Ueberlauf -> IsValid: ", RatIsValid(sum) as int64);
var pi: Rat := RatFromF64(3.14159265358979, 1000000);
ZR("pi (maxDen 1e6) = ", pi);
ZR(" auf maxDen 100 = ", RatApprox(pi, 100));
ZR(" auf maxDen 113 = ", RatApprox(pi, 113));
var buf: int64 := alloc(64);
RatFormatDecimal(RatNew(1, 3), 10, buf, 64);
Print("1/3 dezimal = "); PrintLn(buf as pchar);
return 0;
}
1/3 + 1/6 = 1/2
1/3 * 1/6 = 1/18
1/3 / 1/6 = 2/1
6/8 gekuerzt = 3/4
(1/3)^3 = 1/27
Ueberlauf -> IsValid: 0
pi (maxDen 1e6) = 3126535/995207
auf maxDen 100 = 311/99
auf maxDen 113 = 355/113
1/3 dezimal = 0.3333333333
1/3 + 1/6 ist genau 1/2 — kein Rundungsfehler, keine Toleranz. Jedes Ergebnis wird gekürzt: RatNew(6, 8) ist sofort 3/4.
Überlauf wird gemeldet
<WRAP alert>
Zähler und Nenner sind je ein int64. Bei MAX/2 + MAX/2 passt das Ergebnis nicht mehr — die Unit liefert dann einen ungültigen Bruch statt eines falschen.
RatIsValid ist zu prüfen. Ein ungültiger Bruch hat Nenner 0 und rechnet sich stillschweigend weiter, wenn niemand hinsieht. Das ist der bewusste Entwurf: std.rat baut auf zwei int64 statt auf std.bignum, ist dafür schnell — und meldet die Grenze, statt sie zu überschreiten.
</WRAP>
Wer beliebig große Zähler braucht, rechnet mit std.bignum und bildet den Bruch selbst.
Beste Näherung — mit Semikonvergenten
RatApprox(a, maxDen) sucht den Bruch mit dem größten Nenner unterhalb der Schranke, der a am nächsten kommt.
| Höchstnenner | Ergebnis |
|---|---|
| 113 | 355/113 — die berühmte Näherung, 7 gültige Stellen |
| 100 | 311/99 |
311/99ist die interessante Zeile. Die naheliegende Antwort für „Nenner höchstens 100„ wäre22/7— der bekannteste Kettenbruch-Näherungswert.311/99ist aber näher an π, und ein Verfahren, das nur die Konvergenten des Kettenbruchs betrachtet, findet ihn nicht.std.ratberücksichtigt auch die Semikonvergenten und liefert deshalb tatsächlich die beste Näherung, nicht bloß eine gute.
| Funktion | Zweck |
|---|---|
RatFromF64(x, maxDen) | Fließkommazahl annähern |
RatFromF64Exact(x) | die f64-Zahl exakt als Bruch (Nenner ist eine Zweierpotenz) |
RatApprox(a, maxDen) | vorhandenen Bruch vereinfachen |
RatContinuedFrac / RatFromContinuedFrac | Kettenbruchdarstellung |
RatMediant(a, b) | Mediant zweier Brüche |
RatSimplest(lo, hi) | einfachster Bruch im Intervall |
RatSimplest beantwortet die Frage „welcher Bruch mit kleinstem Nenner liegt zwischen diesen beiden Messwerten“ — die übliche Aufgabe beim Rekonstruieren eines Verhältnisses aus gerundeten Angaben.
Funktionsübersicht
| Gruppe | Funktionen |
|---|---|
| Erzeugen | RatNew · RatFromInt · RatZero · RatOne · RatCopy · RatParse |
| Zerlegen | RatNum · RatDen · RatSign |
| Prüfen | RatIsValid · RatIsZero · RatIsInt |
| Rechnen | RatAdd · RatSub · RatMul · RatDiv · RatPow · RatNeg · RatAbs · RatInv |
| Vergleichen | RatCmp · RatEquals · RatLt · RatLe · RatMin · RatMax |
| Runden | RatFloor · RatCeil · RatTrunc · RatRound · RatRoundHalfEven · RatFrac |
| Umwandeln | RatToF64 · RatFromF64 · RatFromF64Exact |
| Text | RatFormat · RatFormatDecimal |
Der Vergleich läuft nicht über Kreuzmultiplikation inint64.a.num · b.dengegenb.num · a.denwürde bei großen Zählern überlaufen und dann das falsche Vorzeichen liefern — bei zwei bestimmten Brüchen ist genau das in der Testbatterie als Negativkontrolle festgehalten.RatCmprechnet über std.i128 und ist deshalb im ganzen Wertebereich verlässlich.
Abgrenzung
| Aufgabe | Unit |
|---|---|
| Exakte Brüche, moderate Größe | std.rat |
| Geldbeträge mit fester Skala | std.money |
| Beliebig große Ganzzahlen | std.bignum |
| Fließkomma mit Toleranz | std.feq |
Letzte Aktualisierung: 2026-08-16 · alle Beispiele mit lyxc 1.0.21A übersetzt und ausgeführt; die Näherungen gegen die bekannten Kettenbruchwerte geprüft, #1580 mit strace und Speichermessung selbst nachgemessen.
