====== 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.