Money
Import money for currency-aware arithmetic (1.40.0). A money.Money value
stores an arbitrary-size integer count of minor units plus a currency code.
For GBP, 123 minor units means GBP 1.23; JPY has zero fraction digits and KWD
has three. Currency metadata comes from the same pinned data as locale.
import money;
import io;
let price = money.Money("19.99", "GBP");
let total = price.multiply(1.20, "halfEven");
io.println(total); /* GBP 23.99 */
io.println(total.minorUnits); /* 2399 */
io.println(price); /* GBP 19.99: arithmetic returns a new value */
money is now a reserved stdlib module name. If an application already has a
module named money, give it an application namespace before upgrading.
Construction And Exactness
money.Money(amount, currency, rounding = "unnecessary") accepts an int,
decimal, or numeric string. An integer amount is in major units:
money.Money(12, "GBP") is GBP 12.00. Use money.fromMinor(12, "GBP")
for GBP 0.12. Currency codes are trimmed and normalized to uppercase.
Unknown codes and the noncurrency code XXX raise ValueError.
Floats are rejected with TypeError. Use exact decimal literals or strings at
input boundaries. Converting a previously rounded float to decimal cannot
recover digits that were lost earlier.
The read-only fields are currency, minorUnits, and fractionDigits.
amount() returns an exact decimal in major units. There is no fixed maximum
amount imposed by a machine integer size.
By default, construction and arithmetic reject a fractional minor unit with
ValueError. For example, GBP 1.001 cannot be stored exactly. Choose a rounding
mode explicitly when discarding a fractional minor unit is part of your policy.
Totals
money.sum(values, currency = null) totals a list<money.Money> by accumulating
integer minor units, constructing one result. Every value must use the same
currency. An optional currency is normalized and checked against each value.
Empty input requires that currency and returns zero; missing or mixed currencies
raise ValueError.
let total = money.sum([money.Money("0.10", "GBP"), money.Money("0.20", "GBP")]);
io.println(total.minorUnits); /* 30 */
let zero = money.sum([], "JPY");
Arithmetic And Rounding
| Operation | Result |
|---|---|
a + b, a.add(b) |
Sum; currencies must match. |
a - b, a.subtract(b) |
Difference; currencies must match. |
a * factor, a.multiply(factor, rounding = "unnecessary") |
Multiply by an int or decimal. |
a / divisor, a.divide(divisor, rounding = "unnecessary") |
Divide by a nonzero int or decimal. |
-a, a.abs() |
Negation or absolute value, retaining the currency. |
a.compare(b) |
-1, 0, or 1; currencies must match. |
a < b, a <= b, a > b, a >= b |
Compare amounts in the same currency. |
a == b |
Same currency and minor-unit amount; false for another currency or a non-Money value. |
Addition, subtraction and ordering across currencies raise ValueError.
There is no implicit currency conversion. Multiplication and division operators
use exact results only; call the methods to request rounding.
| Mode | Rule |
|---|---|
unnecessary |
Reject a fractional minor unit (default). |
halfEven |
Nearest minor unit; ties go to an even integer. |
halfAwayFromZero |
Nearest; ties go away from zero. |
halfTowardZero |
Nearest; ties go toward zero. |
towardZero |
Truncate the fractional minor unit. |
awayFromZero |
Round any fractional minor unit away from zero. |
floor |
Round toward negative infinity. |
ceil |
Round toward positive infinity. |
let pound = money.Money(1, "GBP");
io.println(pound.divide(3, "halfEven")); /* GBP 0.33 */
io.println(money.Money(1.245, "GBP", "halfEven")); /* GBP 1.24 */
io.println(money.Money(-1.245, "GBP", "floor")); /* GBP -1.25 */
Keep intermediate rates and calculations as decimals when they need more precision than a currency's minor unit. Create or round Money at the boundary your application requires. The library supplies rounding mechanisms; it does not select tax rules, cash-denomination rules or exchange rates.
Allocation Without Losing The Total
split(parts) divides equally among a positive integer number of parts.
allocate(weights) accepts a nonempty list<int> of nonnegative weights with
a positive sum. Both return list<Money> and preserve the original total.
for (share in money.Money(10, "GBP").split(3)) {
io.println(share); /* GBP 3.34, then GBP 3.33, then GBP 3.33 */
}
for (share in money.fromMinor(5, "GBP").allocate([1, 2, 0])) {
io.println(share.minorUnits); /* 2, then 3, then 0 */
}
Allocation first assigns whole minor units, then distributes leftover units to the largest fractional remainders. Input order breaks ties. Zero-weight parts receive zero. Negative amounts allocate the absolute value and negate each share, so refunds follow the same distribution as their positive counterparts.
Display And Serialization
toString() and string interpolation produce a stable code plus fixed-scale
amount, such as GBP 12.30. format(tag = "en") delegates to
locale.formatCurrency, using its supported locale tags.
Formatting does not modify the stored amount.
json.stringify(value) writes an object with string amount and currency
fields, for example {"amount":"12.30","currency":"GBP"}. The amount is a
string to preserve exact large values when another system reads the JSON.
json.parseAs(text, money.Money) validates the same shape and precision; a
numeric amount field is rejected.
See the runnable example.