====== std.i128 — 128-Bit-Ganzzahlen ======
128 Bit als Wortpaar: das volle 64×64-Produkt, Division mit Rest, Dezimalein- und -ausgabe bis 39 Stellen und ''(a·b)/c'' ohne Zwischenüberlauf.
→ [[lyx_-_programmiersprache:units:mathematik|Mathematik-Units]] · [[lyx_-_programmiersprache:units:bits|std.bits]] · [[lyx_-_programmiersprache:units:bignum|std.bignum]] · [[lyx_-_programmiersprache:units:sat|std.sat]]
import std.i128;
Schicht 1, hängt an [[lyx_-_programmiersprache:units:bits|std.bits]]. 44 Funktionen. Alle Beispiele mit ''lyxc 1.0.21A'' übersetzt und ausgeführt.
----
===== Darstellung: kein Typ, sondern ein Puffer =====
**''I128'' ist kein Datentyp.** Es gibt kein ''var x: I128''. Eine 128-Bit-Zahl ist ein **16-Byte-Puffer**, den der Aufrufer stellt, und alle Funktionen nehmen dessen Adresse als ''int64'':
var a: int64 := alloc(16);
I128SetI64(a, 1000000000000000000);
Das Ergebnis einer Rechnung geht in einen **weiteren** Puffer: ''I128Mul(a, b, out)''. Wer ''out'' mit ''a'' gleichsetzt, überschreibt einen Operanden während der Rechnung.
''I128Lo(p)'' und ''I128Hi(p)'' liefern die beiden Wörter einzeln. **Diese Wörter sind Bitmuster, keine Zahlen** — ein Vergleich darauf muss vorzeichenlos sein (''I128LtU64''), denn ''2⁶⁴−1'' ist als ''int64'' gelesen negativ.
----
===== Wofür das gut ist =====
unit main;
import std.io;
import std.string;
import std.alloc;
import std.i128;
fn ZI(t: pchar, v: int64): void { Print(t); PrintLn(IntToStr(v)); }
fn ZD(t: pchar, p: int64): void {
var b: int64 := alloc(64);
I128ToDecimalS(p, b, 64);
Print(t); PrintLn(b as pchar);
}
fn main(): int64 {
var a: int64 := alloc(16);
var b: int64 := alloc(16);
var r: int64 := alloc(16);
I128SetU64(a, 12345678901234567890);
I128SetU64(b, 9876543210987654321);
I128Mul(a, b, r);
ZD("Produkt ueber 128 Bit : ", r);
var roh: int64 := 12345678901234567890 * 9876543210987654321;
ZI("dasselbe roh in int64 : ", roh);
var okAddr: int64 := alloc(8);
var md: int64 := I128MulDivU(1000000000000000000, 3, 7, okAddr);
ZI("MulDivU(1e18, 3, 7) : ", md);
ZI(" ok : ", peek64(okAddr));
I128SetI64(a, 1000000000000000000);
I128Mul(a, a, r);
ZD("1e18 * 1e18 : ", r);
var q: int64 := alloc(16);
var rem: int64 := alloc(16);
I128SetI64(b, 7);
I128DivRemS(r, b, q, rem);
ZD(" geteilt durch 7 : ", q);
ZD(" Rest : ", rem);
ZI("PopCount(1e36) : ", I128PopCount(r));
ZI("HighestSet(1e36) : ", I128HighestSet(r));
ZI("FitsI64(1e36) : ", I128FitsI64(r) as int64);
return 0;
}
Produkt ueber 128 Bit : 121932631137021795223746380111126352690
dasselbe roh in int64 : 133124662968603442
MulDivU(1e18, 3, 7) : 428571428571428571
ok : 1
1e18 * 1e18 : 1000000000000000000000000000000000000
geteilt durch 7 : 142857142857142857142857142857142857
Rest : 1
PopCount(1e36) : 44
HighestSet(1e36) : 119
FitsI64(1e36) : 0
Sämtliche Werte stimmen mit der Referenzrechnung überein — **einschließlich des Überlaufs**: ''133124662968603442'' ist genau das, was von dem 39-stelligen Produkt in 64 Bit übrigbleibt. Ohne Meldung, ohne Anzeichen.
----
===== ''MulDivU'' — der häufigste Anwendungsfall =====
''I128MulDivU(a, b, c, okAddr)'' rechnet ''(a · b) / c'' und gibt das Ergebnis als ''int64'' zurück. Der Zwischenwert ''a · b'' läuft dabei über 128 Bit, das Endergebnis passt wieder in 64.
Genau diese Form braucht man ständig: Prozentrechnung, Umrechnungskurse, Verhältnisse, Skalierung. ''1e18 · 3 / 7'' ist roh nicht ausrechenbar — ''1e18 · 3'' passt schon nicht mehr —, über 128 Bit dagegen problemlos.
**''okAddr'' ist zu prüfen.** Passt das Endergebnis nicht in ''int64'' oder ist ''c'' null, steht dort 0 und der Rückgabewert ist bedeutungslos.
----
===== Funktionsübersicht =====
^ Gruppe ^ Funktionen ^
| Setzen und Lesen | ''I128Set'' · ''I128SetU64'' · ''I128SetI64'' · ''I128Zero'' · ''I128Copy'' · ''I128Lo'' · ''I128Hi'' |
| Prüfen | ''I128IsZero'' · ''I128IsNeg'' · ''I128FitsU64'' · ''I128FitsI64'' |
| Grundrechenarten | ''I128Add'' · ''I128Sub'' · ''I128Mul'' · ''I128MulU64'' · ''I128Neg'' · ''I128Abs'' |
| Volles 64×64-Produkt | ''I128MulHiU'' · ''I128MulHiS'' · ''I128MulLo'' · ''I128Mul64U'' · ''I128Mul64S'' · ''I128MulOverflowsU'' |
| Division | ''I128DivRemU'' · ''I128DivRemS'' · ''I128DivRemU64'' · ''I128MulDivU'' · ''I128AvgU'' |
| Bits | ''I128Shl'' · ''I128ShrU'' · ''I128Sar'' · ''I128PopCount'' · ''I128HighestSet'' · ''I128TestBit'' · ''I128SetBit'' |
| Vergleich | ''I128CmpU'' · ''I128CmpS'' · ''I128Equals'' · ''I128LtU64'' |
| Überträge | ''I128AddCarry'' · ''I128SubBorrow'' |
| Text | ''I128ToDecimalU'' · ''I128ToDecimalS'' · ''I128FromDecimal'' |
''I128ToDecimalS'' behandelt die Zahl als **vorzeichenbehaftet**, ''I128ToDecimalU'' als vorzeichenlos — bei gesetztem obersten Bit ergeben die beiden völlig verschiedene Ausgaben. Der Puffer sollte 40 Byte fassen (39 Stellen plus Vorzeichen plus Null).
''I128AvgU(a, b)'' bildet den Mittelwert **ohne** Überlauf des Zwischenergebnisses — bei Binärsuchen über den vollen ''int64''-Bereich der richtige Weg.
----
===== Warum Bibliothek und nicht Sprachtyp =====
> **Ein echter ''int128'' war eine bewusste Entscheidung dagegen.** Die Hardware liefert das volle 64×64-Produkt bereits in einem Registerpaar, der Codegen wirft die obere Hälfte aber weg. Ein Sprachtyp zöge Registerpaare, ABI, sämtliche Operatoren, Literale, Umwandlungen, vorkompilierte Units und Debug-Informationen nach sich — und eine 128÷128-Division gibt es in Hardware ohnehin nicht.
>
> Die dafür nötigen **Builtins gibt es seit [[https://github.com/SEOLizer/LyX-Compiler/issues/1552|#1552]]** (deckungsgleich mit [[https://github.com/SEOLizer/LyX-Compiler/issues/1548|#1548]]): ''mulhi(a, b)'' und ''umulhi(a, b)'' liefern die oberen 64 Bit des Produkts, ''divrem128(hi, lo, d, remAddr)'' teilt 128 durch 64 und legt den Rest an ''remAddr'' ab. Vorher verwarf der Codegen die obere Hälfte, weil er die Zwei-Operanden-Form ''imul rax, rbx'' benutzte.
----
===== Abgrenzung =====
^ Bereich ^ Unit ^
| bis 2⁶³−1 | roh, mit [[lyx_-_programmiersprache:units:sat|std.sat]] gegen Überlauf |
| **128 Bit** | ''std.i128'' |
| beliebig groß | [[lyx_-_programmiersprache:units:bignum|std.bignum]] |
| exakte Brüche | [[lyx_-_programmiersprache:units:rat|std.rat]] |
| Geldbeträge | [[lyx_-_programmiersprache:units:money|std.money]] |
----
Letzte Aktualisierung: 2026-08-16 · alle Beispiele mit ''lyxc 1.0.21A'' übersetzt und ausgeführt; Produkt, Division, Rest, Popcount und der rohe Überlauf gegen eine unabhängige Referenzrechnung geprüft.