====== std.money — Geldbeträge ======
Beträge als skalierte Ganzzahlen: sieben Rundungsarten, Mehrwertsteuer vor- und rückwärts, Aufteilen ohne verlorene Cents, Formatieren nach Landesart.
→ [[lyx_-_programmiersprache:units:mathematik|Mathematik-Units]] · [[lyx_-_programmiersprache:units:sat|std.sat]] · [[lyx_-_programmiersprache:units:rat|std.rat]] · [[lyx_-_programmiersprache:guides:kassensichv|KassenSichV]]
import std.money;
Schicht 1, hängt an [[lyx_-_programmiersprache:units:sat|std.sat]] und ''std.alloc''. 27 Funktionen, elf Konstanten. Alle Beispiele mit ''lyxc 1.0.21A'' übersetzt und ausgeführt.
----
===== Geld ist keine Fließkommazahl =====
''0,10 + 0,20'' ist in ''f64'' **nicht** ''0,30'' (→ [[lyx_-_programmiersprache:units:feq|std.feq]]). Für eine Rechnung, die stimmen muss, ist das disqualifizierend.
''std.money'' rechnet deshalb in **Ganzzahlen** — der Betrag ist ein ''int64'' in der kleinsten Einheit, die Skala sagt, wo das Komma steht. ''1050'' bei Skala 2 sind ''10,50''. Alle Grundrechenarten sind damit exakt; gerundet wird nur dort, wo es unvermeidlich ist, und dann ausdrücklich.
''MONEY_INVALID'' ist der kleinste ''int64''-Wert und markiert ein ungültiges Ergebnis; ''MoneyIsValid(v)'' fragt danach. Die Grundrechenarten setzen auf ''std.sat'' auf und melden einen Überlauf, statt ihn umschlagen zu lassen.
----
===== Runden: sieben Arten, und warum das nötig ist =====
unit main;
import std.io;
import std.string;
import std.alloc;
import std.money;
fn ZI(t: pchar, v: int64): void { Print(t); PrintLn(IntToStr(v)); }
fn main(): int64 {
ZI("Add(1050, 299) : ", MoneyAdd(1050, 299));
ZI("DivRound(100, 3, HALF_UP) : ", MoneyDivRound(100, 3, MONEY_HALF_UP));
ZI("DivRound(250, 100, HALF_UP) : ", MoneyDivRound(250, 100, MONEY_HALF_UP));
ZI("DivRound(250, 100, HALF_EVEN): ", MoneyDivRound(250, 100, MONEY_HALF_EVEN));
ZI("DivRound(350, 100, HALF_EVEN): ", MoneyDivRound(350, 100, MONEY_HALF_EVEN));
ZI("DivRound(-250, 100, FLOOR) : ", MoneyDivRound(-250, 100, MONEY_FLOOR));
return 0;
}
Add(1050, 299) : 1349
DivRound(100, 3, HALF_UP) : 33
DivRound(250, 100, HALF_UP) : 3
DivRound(250, 100, HALF_EVEN): 2
DivRound(350, 100, HALF_EVEN): 4
DivRound(-250, 100, FLOOR) : -3
**Bei ''2,5'' gehen die Verfahren auseinander, bei ''3,5'' nicht.** ''HALF_EVEN'' rundet zur geraden Nachbarzahl — über viele Beträge hebt sich der Rundungsfehler damit auf, statt sich zu summieren. Das ist der Grund, warum es im Bankwesen und in der Statistik die Vorgabe ist.
^ Konstante ^ Verhalten bei ''x,5'' ^
| ''MONEY_HALF_UP'' | von der Null weg (Schulregel) |
| ''MONEY_HALF_EVEN'' | zur geraden Zahl (kaufmännisch, bankers' rounding) |
| ''MONEY_HALF_DOWN'' | zur Null hin |
| ''MONEY_UP'' | immer von der Null weg |
| ''MONEY_DOWN'' | immer zur Null hin (abschneiden) |
| ''MONEY_FLOOR'' | immer nach unten |
| ''MONEY_CEIL'' | immer nach oben |
''MONEY_DOWN'' und ''MONEY_FLOOR'' unterscheiden sich **nur bei negativen Beträgen**: ''−2,5'' wird zu ''−2'' beziehungsweise ''−3''.
''MoneyRoundTo(v, scale, places, mode)'' rundet auf eine Stellenzahl, ''MoneyRoundToMultiple(v, mult, mode)'' auf ein Vielfaches — für die Fünf-Rappen-Rundung in der Schweiz oder Preisenden auf 0,99.
----
===== Mehrwertsteuer in beide Richtungen =====
var brutto: int64 := MoneyAddTax(10000, 1900, 4, MONEY_HALF_UP);
var steuer: int64 := MoneyTaxOfGross(11900, 1900, 4, MONEY_HALF_UP);
var netto: int64 := MoneyNetOfGross(11900, 1900, 4, MONEY_HALF_UP);
AddTax(10000, 19%) : 11900
TaxOfGross(11900, 19%): 1900
NetOfGross(11900, 19%): 10000
Der Satz wird als **skalierte Ganzzahl** übergeben: ''1900'' bei ''rateScale'' 4 sind 19 %. Damit lassen sich auch krumme Sätze wie 8,25 % exakt darstellen (''825'' bei Skala 4).
> **Rückwärts ist nicht dasselbe wie vorwärts.** Aus dem Bruttobetrag den Steueranteil zu ziehen ist eine andere Rechnung als den Nettobetrag zu erhöhen — deshalb gibt es dafür eigene Funktionen. Wer ''brutto − brutto/1,19'' selbst rechnet, bekommt bei manchen Beträgen einen Cent Abweichung gegenüber dem, was das Finanzamt erwartet.
''MoneyMulRate'' und ''MoneyPercent'' sind die allgemeinen Formen für Kurse und Prozentsätze.
----
===== Aufteilen ohne Centverlust =====
var out: int64 := alloc(3 * 8);
MoneySplit(1000, 3, out);
Split(1000, 3) : 334 333 333 Summe=1000
**Die Summe der Teile ist wieder der Ausgangsbetrag.** ''10,00 €'' durch drei geteilt ergibt nicht dreimal ''3,33'' (das wären ''9,99''), sondern ''3,34 + 3,33 + 3,33''. Der Rest wird auf die vorderen Teile verteilt, statt zu verschwinden.
''MoneyAllocate(total, weights, n, out)'' macht dasselbe **gewichtet** — für Rabatte, Umlagen und Skonti, die anteilig auf Positionen verteilt werden müssen, ohne dass am Ende ein Cent fehlt.
----
===== Formatieren und Einlesen =====
var buf: int64 := alloc(64);
MoneyFormat(123456, 2, buf, 64, MONEY_FMT_DE);
Format(123456, DE) : 1.234,56
Format(123456, CH) : 1'234.56
^ Konstante ^ Ergebnis ^
| ''MONEY_FMT_PLAIN'' | ''1234.56'' |
| ''MONEY_FMT_DE'' | ''1.234,56'' |
| ''MONEY_FMT_EN'' | ''1,234.56'' |
| ''MONEY_FMT_CH'' | ''1'234.56'' |
''MoneyParse(s, scale, mode)'' liest zurück und liefert ''MONEY_INVALID'', wenn die Zeichenkette nicht passt.
----
===== Währungen =====
MoneyCurrencyScale("EUR"c) // 2
MoneyCurrencyScale("JPY"c) // 0
MoneyCurrencyScale("KWD"c) // 3
CurrencyScale(EUR) : 2
CurrencyScale(JPY) : 0
CurrencyScale(KWD) : 3
**Der Parameter ist die Adresse einer Zeichenkette, kein Zahlencode.** ''MoneyCurrencyScale("EUR"c)'' — wer eine beliebige Zahl übergibt, liest an dieser Adresse und bekommt einen **Speicherzugriffsfehler**. Die Funktion fängt nur den Fall ''0'' ab.
Yen und Won haben **keine** Nachkommastellen, die Golfwährungen (KWD, BHD, OMR) drei. Genau diese Ausreißer sind der Grund, warum die Skala überhaupt ein Parameter ist — wer fest mit Cents rechnet, liegt bei jedem Yen-Betrag um den Faktor 100 daneben. Unbekannte Codes bekommen 2.
Erfasst sind derzeit nur die Ausreißer; eine vollständige ISO-4217-Tabelle gibt es nicht.
----
===== Funktionsübersicht =====
^ Gruppe ^ Funktionen ^
| Grundrechenarten | ''MoneyAdd'' · ''MoneySub'' · ''MoneyNeg'' · ''MoneyAbs'' · ''MoneyMulInt'' · ''MoneyDivInt'' · ''MoneySum'' |
| Vergleichen | ''MoneyCmp'' · ''MoneySign'' · ''MoneyMin'' · ''MoneyMax'' · ''MoneyIsValid'' |
| Runden | ''MoneyDivRound'' · ''MoneyRoundTo'' · ''MoneyRoundToMultiple'' · ''MoneyPow10'' |
| Skala wechseln | ''MoneyRescale'' |
| Sätze und Steuern | ''MoneyMulRate'' · ''MoneyPercent'' · ''MoneyAddTax'' · ''MoneyTaxOfGross'' · ''MoneyNetOfGross'' |
| Verteilen | ''MoneySplit'' · ''MoneyAllocate'' |
| Text | ''MoneyParse'' · ''MoneyFormat'' · ''MoneyCurrencyScale'' |
----
Letzte Aktualisierung: 2026-08-16 · alle Beispiele mit ''lyxc 1.0.21A'' übersetzt und ausgeführt; Rundungsarten, Steuerrechnung in beide Richtungen und die Summenerhaltung beim Aufteilen einzeln nachgemessen.