====== std.rat — exakte Brüche ====== Rationale Zahlen auf zwei ''int64''. Jeder Überlauf wird **gemeldet** statt abgeschnitten; beste Näherung mit Semikonvergenten. → [[lyx_-_programmiersprache:units:mathematik|Mathematik-Units]] · [[lyx_-_programmiersprache:units:i128|std.i128]] · [[lyx_-_programmiersprache:units:money|std.money]] · [[lyx_-_programmiersprache:units:feq|std.feq]] import std.rat; Schicht 2, hängt an [[lyx_-_programmiersprache:units:i128|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** ([[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 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 [[lyx_-_programmiersprache:units:complex|std.complex]] und [[lyx_-_programmiersprache:units:quat|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 ===== 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 [[lyx_-_programmiersprache:units:bignum|std.bignum]], ist dafür schnell — und meldet die Grenze, statt sie zu überschreiten. 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/99'' ist die interessante Zeile.** Die naheliegende Antwort für „Nenner höchstens 100" wäre ''22/7'' — der bekannteste Kettenbruch-Näherungswert. ''311/99'' ist aber **näher** an π, und ein Verfahren, das nur die Konvergenten des Kettenbruchs betrachtet, findet ihn nicht. ''std.rat'' berü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 in ''int64''.** ''a.num · b.den'' gegen ''b.num · a.den'' wü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. ''RatCmp'' rechnet über [[lyx_-_programmiersprache:units:i128|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 | [[lyx_-_programmiersprache:units:money|std.money]] | | Beliebig große Ganzzahlen | [[lyx_-_programmiersprache:units:bignum|std.bignum]] | | Fließkomma mit Toleranz | [[lyx_-_programmiersprache:units:feq|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.