Money<TCurrency> Struct
Definition
Represents an immutable monetary amount denominated in the currency identified by TCurrency.
public readonly struct Money<TCurrency> : ISpanFormattable, IFormattable, IUtf8SpanFormattable, IEquatable<Money<TCurrency>>, IComparable<Money<TCurrency>>, IComparable, ISpanParsable<Money<TCurrency>>, IParsable<Money<TCurrency>> where TCurrency : ICurrency
Type Parameters
TCurrencyA type implementing ICurrency that identifies the currency at the type-system level. Use one of the shipped tag types in
Bodu.Financial.Currencies(for example,Money<Bodu.Financial.Currencies.USD>).
- Implements
-
IEquatable<Money<TCurrency>>IComparable<Money<TCurrency>>ISpanParsable<Money<TCurrency>>
- Inherited Members
- Extension Methods
Remarks
Money<TCurrency> represents a settlement-grade monetary value - an amount already rounded to
the currency's minor-unit precision and ready to be posted to a ledger, displayed to a user, or serialized to
storage. It is deliberately not a calculation type: every operation that produces a Money<TCurrency>
returns a value that respects TCurrency.MinorUnits, so chained scalar operations round at each step rather
than accumulating sub-minor-unit precision.
The amount is stored as a decimal rounded on construction to TCurrency.MinorUnits using
banker's rounding (ToEven). Two Money<TCurrency> values that
represent the same monetary amount compare equal regardless of the input expressions that produced them.
Arithmetic between two Money<TCurrency> instances is permitted only when both operands share the same
TCurrency. Cross-currency addition, subtraction, and comparison are compile errors, not
runtime exceptions; cross-currency conversion is available exclusively through the explicit
Convert<TTarget>(decimal, MidpointRounding) method.
Scalar multiplication and division round their result to TCurrency.MinorUnits. For chains where rounding at
each step would accumulate error - compound interest, unit-rate products, percentages - defer rounding: use
CalculatedMoney for high-precision decimal calculation rounded once at settlement, or,
when the calculation must be mathematically exact, perform it in Fraction<T> via
ToFraction() and snap to Money<TCurrency> only at the final settlement boundary with
FromFraction(Fraction<BigInteger>, MidpointRounding) or the
MultiplyExact(Fraction<BigInteger>, MidpointRounding) shortcut.
The default value (default(Money<TCurrency>)) represents zero of the given currency without invoking
the rounding constructor, so it is safe even when TCurrency reports invalid minor-unit
metadata.
JSON serialization ships in the companion Bodu.Financial.Serialization.Json package; the type carries no
[JsonConverter] attribute, so register the financial converters on the target JsonSerializerOptions
via its AddFinancialJsonConverters extension.
using Bodu.Financial;
using Bodu.Financial.Currencies;
// The currency is part of the type, so the compiler tracks it for you.
var price = new Money<USD>(19.999m); // 20.00 USD (banker's rounding on construction)
var tax = new Money<USD>(1.60m);
// Same-currency arithmetic is checked at compile time.
Money<USD> total = price + tax; // 21.60 USD
bool isDearer = total > price; // true
// Scaling re-rounds the product to the minor unit.
Money<USD> tripled = price * 3m; // 60.00 USD
// Cross-currency mixing is a compile error - use Convert for an explicit FX conversion.
var euros = price.Convert<EUR>(0.92m); // 18.40 EUR
// _ = total + new Money<EUR>(5m); // does not compile
Constructors
Money(decimal)
Initializes a new instance of the Money<TCurrency> struct, rounding amount to
TCurrency.MinorUnits using banker's rounding.
public Money(decimal amount)
Parameters
amountdecimalThe monetary amount in the major unit of
TCurrency.
Remarks
The supplied amount is rounded to the currency's minor-unit precision using banker's rounding - midpoint values
round toward the nearer even final digit, in both directions. For example, new Money<USD>(1.225m)
is stored as 1.22m (down toward the even hundredth 2) while new Money<USD>(1.235m) is
stored as 1.24m (up toward the even hundredth 4). Non-midpoint values round in the natural
direction: new Money<JPY>(99.6m) is stored as 100m.
To round with a different rule, use the Money(decimal, MidpointRounding) overload.
Exceptions
- InvalidOperationException
Thrown when
TCurrencyreports invalid metadata - an IsoCode that is not exactly three uppercase ASCII letters, an MinorUnits outside the inclusive range[0, 28], or a CashRoundingIncrement that is negative or finer than the currency's minor-unit precision.
Money(decimal, MidpointRounding)
Initializes a new instance of the Money<TCurrency> struct, rounding amount to
TCurrency.MinorUnits using the specified midpoint-rounding rule.
public Money(decimal amount, MidpointRounding rounding)
Parameters
amountdecimalThe monetary amount in the major unit of
TCurrency.roundingMidpointRoundingThe rule used to round midpoint values.
Exceptions
- InvalidOperationException
Thrown when
TCurrencyreports invalid metadata.
Properties
Amount
Gets the rounded monetary amount in the major unit of TCurrency.
public decimal Amount { get; }
Property Value
- decimal
The amount stored by this instance, rounded to MinorUnits decimal places.
CashRoundingIncrement
Gets the smallest cash denomination of TCurrency, expressed in the major unit.
public static decimal CashRoundingIncrement { get; }
Property Value
- decimal
The cash-rounding increment in the major unit (for example,
0.05mfor CHF), or0mwhen the currency does not require special cash rounding beyond its MinorUnits precision.
Remarks
A return value of 0m indicates that cash totals use the same precision as electronic totals, and
RoundToCash(MidpointRounding) is a no-op for the currency.
DemonetizedOn
Gets the date TCurrency was withdrawn from circulation, if known.
public static DateOnly? DemonetizedOn { get; }
Property Value
IsHistoric
Gets a value indicating whether TCurrency has been demonetized.
public static bool IsHistoric { get; }
Property Value
IsoCode
Gets the ISO 4217 alphabetic code of TCurrency.
public static string IsoCode { get; }
Property Value
- string
The currency code, such as
"USD".
Exceptions
- InvalidOperationException
Thrown when
TCurrencyreports invalid metadata.
MinorUnits
Gets the minor-unit precision of TCurrency.
public static int MinorUnits { get; }
Property Value
- int
The non-negative number of fractional digits the currency uses.
Exceptions
- InvalidOperationException
Thrown when
TCurrencyreports invalid metadata.
SuccessorIsoCode
Gets the ISO 4217 alphabetic code of the currency that replaced TCurrency, when
applicable.
public static string? SuccessorIsoCode { get; }
Property Value
Zero
Gets a Money<TCurrency> representing zero of TCurrency.
public static Money<TCurrency> Zero { get; }
Property Value
- Money<TCurrency>
The zero monetary amount.
Methods
Allocate(int)
Distributes this amount as evenly as possible across parts shares, in a way that the shares
sum exactly to the original amount.
public Money<TCurrency>[] Allocate(int parts)
Parameters
partsintThe number of shares to allocate. Must be greater than zero.
Returns
- Money<TCurrency>[]
An array of
partsMoney<TCurrency> values whose sum equals this instance. Any residual minor units are distributed one per share from the start of the array, preserving the sign of the original amount.
Examples
using Bodu.Financial;
using Bodu.Financial.Currencies;
// Split a bill three ways without losing a cent.
Money<USD>[] shares = new Money<USD>(10m).Allocate(3); // [3.34, 3.33, 3.33]
Money<USD> total = shares[0] + shares[1] + shares[2]; // 10.00 USD (exact)
Remarks
For example, new Money<USD>(0.10m).Allocate(3) returns [0.04, 0.03, 0.03], and
new Money<USD>(-10m).Allocate(3) returns [-3.34, -3.33, -3.33]. When the amount has fewer
minor units than parts, trailing shares are zero (for example,
new Money<USD>(0.02m).Allocate(5) returns [0.01, 0.01, 0, 0, 0]).
Exceptions
- ArgumentOutOfRangeException
Thrown when
partsis less than or equal to zero.
Allocate(ReadOnlySpan<decimal>)
Distributes this amount across the supplied non-negative ratios in proportion to their
magnitudes, ensuring the resulting shares sum exactly to the original amount.
public Money<TCurrency>[] Allocate(ReadOnlySpan<decimal> ratios)
Parameters
ratiosReadOnlySpan<decimal>The non-negative weight applied to each share. At least one weight must be strictly positive; zero weights produce a zero share.
Returns
- Money<TCurrency>[]
An array of Money<TCurrency> values whose length equals
ratios. Length and whose sum equals this instance. Residual minor units are distributed by the largest-remainder method - each slot receives one extra unit in descending order of its fractional remainder, with ties broken by stable input order. Zero-ratio slots never receive residual.
Exceptions
- ArgumentException
Thrown when
ratiosis empty, contains a negative element, or sums to zero.
CompareTo(Money<TCurrency>)
Compares this instance to other by amount.
public int CompareTo(Money<TCurrency> other)
Parameters
otherMoney<TCurrency>The instance to compare against.
Returns
- int
A negative value when this instance is smaller, zero when the two are equal, and a positive value when this instance is greater.
CompareTo(object?)
Compares this instance to obj when it is a same-currency Money<TCurrency>.
public int CompareTo(object? obj)
Parameters
objobjectThe boxed value to compare against.
Returns
- int
The result of CompareTo(Money<TCurrency>) when
objmatches; the comparison rules of IComparable whenobjis null.
Exceptions
- ArgumentException
Thrown when
objis not a Money<TCurrency> over the sameTCurrency.
Convert<TQuote>(ExchangeRate<TCurrency, TQuote>, MidpointRounding)
Converts this amount to TQuote at the supplied typed exchange rate, rounding the result
to the destination currency's minor-unit precision.
public Money<TQuote> Convert<TQuote>(ExchangeRate<TCurrency, TQuote> exchangeRate, MidpointRounding rounding = MidpointRounding.ToEven) where TQuote : ICurrency
Parameters
exchangeRateExchangeRate<TCurrency, TQuote>A strongly-typed rate whose base must match
TCurrency.roundingMidpointRoundingThe midpoint-rounding rule applied when narrowing to the target precision.
Returns
- Money<TQuote>
The converted monetary amount in
TQuote.
Type Parameters
TQuoteThe destination currency, fixed by the supplied rate's quote leg.
Remarks
This overload is the typed counterpart of Convert<TTarget>(decimal, MidpointRounding). The rate's direction is enforced at compile time, eliminating the class of bug where a caller multiplies by an inverted-direction rate.
Convert<TTarget>(decimal, MidpointRounding)
Converts this amount to TTarget at the supplied exchange rate, rounding the result to
the minor-unit precision of TTarget.
public Money<TTarget> Convert<TTarget>(decimal exchangeRate, MidpointRounding rounding = MidpointRounding.ToEven) where TTarget : ICurrency
Parameters
exchangeRatedecimalThe exchange rate, expressed as units of
TTargetper single unit ofTCurrency. Must be strictly positive.roundingMidpointRoundingThe midpoint-rounding rule applied when narrowing to the target precision.
Returns
- Money<TTarget>
The converted monetary amount in
TTarget.
Type Parameters
TTargetThe destination currency.
Examples
using Bodu.Financial;
using Bodu.Financial.Currencies;
var usd = new Money<USD>(100m);
// Rate is units of the target per single unit of the source currency.
Money<EUR> euros = usd.Convert<EUR>(0.92m); // 92.00 EUR
Money<JPY> yen = usd.Convert<JPY>(157.0m); // 15700 JPY (rounded to JPY's 0 minor units)
Exceptions
- ArgumentOutOfRangeException
Thrown when
exchangeRateis zero or negative. A zero rate is treated as a programmer error in real FX code; for the rare legitimate scalar-zero case, construct the destination amount directly.- OverflowException
Thrown when
amount × exchangeRatefalls outside the range of decimal.
Divide(decimal)
Divides this amount by divisor using banker's rounding, identical to the / operator.
public Money<TCurrency> Divide(decimal divisor)
Parameters
divisordecimalThe scalar divisor.
Returns
- Money<TCurrency>
The quotient, rounded to
TCurrency.MinorUnitsusing banker's rounding.
Exceptions
- DivideByZeroException
divisoris zero.- OverflowException
The quotient falls outside the range of decimal.
Divide(decimal, MidpointRounding)
Divides this amount by divisor and rounds the result to the currency's minor-unit precision
using the supplied rule.
public Money<TCurrency> Divide(decimal divisor, MidpointRounding rounding)
Parameters
divisordecimalThe scalar divisor.
roundingMidpointRoundingThe midpoint-rounding rule applied to the quotient.
Returns
- Money<TCurrency>
The quotient, rounded to
TCurrency.MinorUnitsusingrounding.
Exceptions
- DivideByZeroException
divisoris zero.- OverflowException
The quotient falls outside the range of decimal.
Equals(Money<TCurrency>)
Determines whether this instance equals other in both currency and amount.
public bool Equals(Money<TCurrency> other)
Parameters
otherMoney<TCurrency>The monetary value to compare against.
Returns
Equals(object?)
Determines whether this instance equals the boxed obj reference. Returns
false for any Money<TCurrency> instance over a different currency.
public override bool Equals(object? obj)
Parameters
objobjectThe object to compare against.
Returns
- bool
true when
objis a Money<TCurrency> over the same currency and equal in amount; otherwise false.
FromFraction(Fraction<BigInteger>, MidpointRounding)
Creates a Money<TCurrency> from an exact rational value, rounding to the currency's minor-unit precision using the specified rule. The rounding is performed directly in BigInteger arithmetic without an intermediate decimal step, so values that exceed decimal's 28-digit precision retain their exact rounding direction.
public static Money<TCurrency> FromFraction(Fraction<BigInteger> value, MidpointRounding rounding = MidpointRounding.ToEven)
Parameters
valueFraction<BigInteger>The exact amount.
roundingMidpointRoundingThe midpoint-rounding rule.
Returns
- Money<TCurrency>
The rounded monetary amount.
Exceptions
- OverflowException
Thrown when the rounded result cannot be represented as a decimal.
FromMinorUnits(long)
Creates a Money<TCurrency> from an exact integer count of minor units (cents, yen, fils, etc.).
public static Money<TCurrency> FromMinorUnits(long minorUnits)
Parameters
minorUnitslongThe number of minor units in the major unit's smallest denomination.
Returns
- Money<TCurrency>
The corresponding monetary amount.
Remarks
Ledger storage and wire formats often persist money as integer minor units. FromMinorUnits(long)
is the canonical bridge from that representation. For example, Money<USD>.FromMinorUnits(1999) is
$19.99; Money<JPY>.FromMinorUnits(1234) is ¥1,234;
Money<BHD>.FromMinorUnits(12345) is 12.345 BHD.
GetHashCode()
Returns a hash code consistent with Equals(Money<TCurrency>) - combining the closed generic type identity (not the runtime IsoCode string) with the amount so hashing matches equality even when two different currency tag types share an ISO code or when a custom tag mutates its reported code.
public override int GetHashCode()
Returns
- int
A 32-bit hash code suitable for use in hash-based collections.
Multiply(decimal)
Multiplies this amount by multiplier using banker's rounding, identical to the *
operator.
public Money<TCurrency> Multiply(decimal multiplier)
Parameters
multiplierdecimalThe scalar multiplier.
Returns
- Money<TCurrency>
The product, rounded to
TCurrency.MinorUnitsusing banker's rounding.
Remarks
Use the Multiply(decimal, MidpointRounding) overload when a rounding rule other than banker's rounding is required.
Exceptions
- OverflowException
The product falls outside the range of decimal.
Multiply(decimal, MonetaryContext)
Multiplies this amount by multiplier and rounds the result to the currency's minor-unit
precision using context's rounding strategy.
public Money<TCurrency> Multiply(decimal multiplier, MonetaryContext context)
Parameters
multiplierdecimalThe scalar multiplier.
contextMonetaryContextThe monetary context whose rounding strategy is applied at the settlement boundary.
Returns
- Money<TCurrency>
The product, rounded to
TCurrency.MinorUnitsusingcontext.
Remarks
Because Money<TCurrency> is a settlement value, only the context's rounding strategy is honoured; the scale is fixed to the currency's minor units. For deferred-rounding chains use a high-precision calculation value and round once at the end.
Exceptions
- ArgumentNullException
contextis null.- OverflowException
The product falls outside the range of decimal.
Multiply(decimal, MidpointRounding)
Multiplies this amount by multiplier and rounds the result to the currency's minor-unit
precision using the supplied rule.
public Money<TCurrency> Multiply(decimal multiplier, MidpointRounding rounding)
Parameters
multiplierdecimalThe scalar multiplier.
roundingMidpointRoundingThe midpoint-rounding rule applied to the product.
Returns
- Money<TCurrency>
The product, rounded to
TCurrency.MinorUnitsusingrounding.
Examples
using Bodu.Financial;
using Bodu.Financial.Currencies;
var price = new Money<USD>(2.125m); // stored as 2.12 (banker's rounding)
// Apply a retail-style 8.25% tax, rounding the product away from zero.
Money<USD> tax = price.Multiply(0.0825m, MidpointRounding.AwayFromZero);
Remarks
Use this overload when a non-default rounding rule (for example, AwayFromZero
for retail-tax workflows) must be applied. The * operator forces banker's rounding because operator
signatures cannot accept a rounding-mode parameter.
Exceptions
- OverflowException
The product falls outside the range of decimal.
MultiplyExact(Fraction<BigInteger>, MidpointRounding)
Multiplies this amount by an exact rational factor and rounds the result to the currency's minor-unit precision.
public Money<TCurrency> MultiplyExact(Fraction<BigInteger> factor, MidpointRounding rounding = MidpointRounding.ToEven)
Parameters
factorFraction<BigInteger>The exact multiplier.
roundingMidpointRoundingThe midpoint-rounding rule.
Returns
- Money<TCurrency>
The product rounded to
TCurrency.MinorUnits.
Remarks
Equivalent to FromFraction(ToFraction() * factor, rounding) but expresses the common multiply-then-round
pattern in a single call. Inherits the exact BigInteger rounding behaviour of
FromFraction(Fraction<BigInteger>, MidpointRounding).
Exceptions
- OverflowException
Thrown when the result cannot be represented as a decimal.
RatioTo(Money<TCurrency>)
Returns the dimensionless ratio of this amount to other.
public decimal RatioTo(Money<TCurrency> other)
Parameters
otherMoney<TCurrency>The denominator amount, in the same currency.
Returns
- decimal
The dimensionless ratio
this.Amount / other.Amount.
Remarks
Provides a readable named alternative to the Money<TCurrency> / Money<TCurrency> operator,
which returns the same value. Both forms are equivalent.
Exceptions
- DivideByZeroException
otherhas an amount of zero.
Round(int, MidpointRounding)
Returns a new Money<TCurrency> with the amount rounded to decimals fractional
digits using the specified rule.
public Money<TCurrency> Round(int decimals, MidpointRounding rounding = MidpointRounding.ToEven)
Parameters
decimalsintThe number of fractional digits to keep. Must be non-negative and not greater than the currency's MinorUnits.
roundingMidpointRoundingThe midpoint-rounding rule.
Returns
- Money<TCurrency>
The rounded amount.
Remarks
Use this to coarsen below the currency's natural precision - for example, rounding USD to whole dollars before display. Rounding above MinorUnits has no effect because the stored amount is already limited to the minor-unit precision.
Exceptions
- ArgumentOutOfRangeException
Thrown when
decimalsis negative or greater than MinorUnits.
RoundToCash()
Rounds this amount to the nearest physical cash denomination of TCurrency using banker's
rounding.
public Money<TCurrency> RoundToCash()
Returns
- Money<TCurrency>
A Money<TCurrency> whose amount is the nearest multiple of CashRoundingIncrement, or this instance unchanged when the currency does not declare a cash-rounding increment.
Remarks
Cash rounding applies to physical cash totals only; electronic transactions retain the full
MinorUnits precision. For Switzerland (CHF, 5-rappen rounding), Canada (CAD, 5-cent cash rounding
since 2013), Australia (AUD, 5-cent cash rounding since 1992), and other currencies whose smallest circulating
coin is larger than 10^-MinorUnits, this method snaps to the cash denomination - for example,
CHF 12.34 rounds to CHF 12.35.
RoundToCash(MidpointRounding)
Rounds this amount to the nearest physical cash denomination of TCurrency using the
specified midpoint-rounding rule.
public Money<TCurrency> RoundToCash(MidpointRounding rounding)
Parameters
roundingMidpointRoundingThe midpoint-rounding rule applied when the amount sits exactly between two cash denominations.
Returns
- Money<TCurrency>
A Money<TCurrency> whose amount is the nearest multiple of CashRoundingIncrement, or this instance unchanged when the currency does not declare a cash-rounding increment.
ToCalculated()
Returns a high-precision runtime-tagged CalculatedMoney carrying this value's amount and the
currency derived from TCurrency, suitable for deferred-rounding calculation chains.
public CalculatedMoney ToCalculated()
Returns
- CalculatedMoney
A CalculatedMoney with this value's amount.
ToFraction()
Converts this amount to an exact Fraction<T> over BigInteger for arithmetic that must not round at each step.
public Fraction<BigInteger> ToFraction()
Returns
- Fraction<BigInteger>
The amount as an exact rational value.
Remarks
Use ToFraction() together with FromFraction(Fraction<BigInteger>, MidpointRounding) to defer rounding until the end of a calculation chain. For example, multiplying by an exchange-rate fraction and a fee-percentage fraction before converting back to Money<TCurrency> rounds only once on return.
ToMinorUnits()
Returns this amount as an integer count of minor units.
public long ToMinorUnits()
Returns
- long
The amount expressed in minor units.
Exceptions
- OverflowException
The scaled value falls outside the range of long. For practical money amounts (up to trillions of major units) this is unreachable; the exception covers pathological values built from near- MaxValue inputs.
ToMoney()
Converts this strongly-typed amount to its runtime-tagged Money equivalent.
public Money ToMoney()
Returns
Remarks
The conversion is lossless: TCurrency determines the currency, which is encoded into the
runtime Code. An implicit operator is also provided so typed values flow into
runtime APIs without an explicit cast.
ToString()
Returns the default string representation: the ISO 4217 code followed by the amount with the currency's minor-unit precision, using thousand separators from the current culture.
public override string ToString()
Returns
- string
A string such as
"USD 1,234.56","JPY 100", or"BHD 12.345".
ToString(string?)
Returns a string representation of this amount using the supplied format specifier.
public string ToString(string? format)
Parameters
formatstringThe format specifier; see Bodu.Financial.Money`1.Format(System.ReadOnlySpan{System.Char},System.IFormatProvider) for the supported vocabulary.
Returns
- string
The formatted representation.
Examples
using Bodu.Financial;
using Bodu.Financial.Currencies;
var amount = new Money<USD>(1234.56m);
string general = amount.ToString(); // "USD 1,234.56" (current culture grouping)
string bare = amount.ToString("F"); // "1234.56" (no designator, no grouping)
string roundTrip = amount.ToString("R"); // "USD 1234.56" (invariant, parser-friendly)
Exceptions
- FormatException
Thrown when
formatis not a supported specifier.
ToString(string?, IFormatProvider?)
Returns a string representation of this amount using the supplied format specifier and culture.
public string ToString(string? format, IFormatProvider? formatProvider)
Parameters
formatstringThe format specifier.
formatProviderIFormatProviderThe culture used to render the numeric component.
Returns
- string
The formatted representation.
Exceptions
- FormatException
Thrown when
formatis not a supported specifier.
TryFormat(Span<byte>, out int, ReadOnlySpan<char>, IFormatProvider?)
Attempts to format this amount into the provided UTF-8 byte span.
public bool TryFormat(Span<byte> utf8Destination, out int bytesWritten, ReadOnlySpan<char> format, IFormatProvider? provider)
Parameters
utf8DestinationSpan<byte>The span that receives the formatted UTF-8 bytes.
bytesWrittenintWhen this method returns, contains the number of bytes written.
formatReadOnlySpan<char>The format specifier.
providerIFormatProviderThe culture used to render the numeric component.
Returns
Exceptions
- FormatException
Thrown when
formatis not a supported specifier.
TryFormat(Span<char>, out int, ReadOnlySpan<char>, IFormatProvider?)
Attempts to format this amount into the provided character span.
public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format, IFormatProvider? provider)
Parameters
destinationSpan<char>The span that receives the formatted characters.
charsWrittenintWhen this method returns, contains the number of characters written.
formatReadOnlySpan<char>The format specifier.
providerIFormatProviderThe culture used to render the numeric component.
Returns
Exceptions
- FormatException
Thrown when
formatis not a supported specifier.
TryToMinorUnits(out long)
Attempts to express this amount as an integer count of minor units, returning false instead of throwing when the value would overflow long.
public bool TryToMinorUnits(out long minorUnits)
Parameters
minorUnitslongOn success, the amount in minor units; otherwise the default value.
Returns
Operators
operator +(Money<TCurrency>, Money<TCurrency>)
Adds two monetary values denominated in the same currency.
public static Money<TCurrency> operator +(Money<TCurrency> left, Money<TCurrency> right)
Parameters
Returns
- Money<TCurrency>
The sum of
leftandright.
Remarks
Cross-currency addition is a compile error because the operator signature requires both operands to share
TCurrency. Convert one side with
Convert<TTarget>(decimal, MidpointRounding) when the operands are in different currencies.
Exceptions
- OverflowException
The sum falls outside the range of decimal.
operator /(Money<TCurrency>, Money<TCurrency>)
Returns the dimensionless ratio of two monetary values denominated in the same currency.
public static decimal operator /(Money<TCurrency> left, Money<TCurrency> right)
Parameters
Returns
- decimal
The dimensionless ratio
left.Amount / right.Amount.
Remarks
Equivalent to RatioTo(Money<TCurrency>); use the named method when the operator is ambiguous in domain code or when the ratio is the primary computation being expressed.
Exceptions
- DivideByZeroException
righthas an amount of zero.
operator /(Money<TCurrency>, decimal)
Divides a monetary value by a scalar, rounding the result to the currency's minor-unit precision using banker's rounding (ToEven).
public static Money<TCurrency> operator /(Money<TCurrency> left, decimal right)
Parameters
Returns
- Money<TCurrency>
The quotient, rounded to
TCurrency.MinorUnitsusing banker's rounding.
Remarks
Scalar division rounds at every call. To divide an amount into shares while preserving the total exactly, use Allocate(int) or Allocate(ReadOnlySpan<decimal>) .
For chains involving division that must avoid per-step rounding drift, perform the calculation through ToFraction() and snap back to Money<TCurrency> only at the final step via FromFraction(Fraction<BigInteger>, MidpointRounding). To control the rounding rule on a single scalar division, use Divide(decimal, MidpointRounding) in place of the operator.
Exceptions
- DivideByZeroException
rightis zero.- OverflowException
The quotient falls outside the range of decimal.
operator ==(Money<TCurrency>, Money<TCurrency>)
Determines whether two monetary values are equal in amount.
public static bool operator ==(Money<TCurrency> left, Money<TCurrency> right)
Parameters
Returns
explicit operator Money<TCurrency>(Money)
Explicitly converts a runtime-tagged Money to Money<TCurrency> when the runtime
currency matches TCurrency.
public static explicit operator Money<TCurrency>(Money value)
Parameters
valueMoneyThe runtime-tagged money.
Returns
- Money<TCurrency>
The strongly-typed equivalent.
Exceptions
- InvalidOperationException
value's Code does not match the currency ofTCurrency.
operator >(Money<TCurrency>, Money<TCurrency>)
Determines whether one monetary value is strictly greater than another in the same currency.
public static bool operator >(Money<TCurrency> left, Money<TCurrency> right)
Parameters
Returns
operator >=(Money<TCurrency>, Money<TCurrency>)
Determines whether one monetary value is greater than or equal to another in the same currency.
public static bool operator >=(Money<TCurrency> left, Money<TCurrency> right)
Parameters
Returns
implicit operator Money(Money<TCurrency>)
Implicitly converts a Money<TCurrency> to a runtime-tagged Money.
public static implicit operator Money(Money<TCurrency> value)
Parameters
valueMoney<TCurrency>The strongly-typed money.
Returns
- Money
The runtime-tagged equivalent.
operator !=(Money<TCurrency>, Money<TCurrency>)
Determines whether two monetary values differ in amount.
public static bool operator !=(Money<TCurrency> left, Money<TCurrency> right)
Parameters
Returns
operator <(Money<TCurrency>, Money<TCurrency>)
Determines whether one monetary value is strictly less than another in the same currency.
public static bool operator <(Money<TCurrency> left, Money<TCurrency> right)
Parameters
Returns
operator <=(Money<TCurrency>, Money<TCurrency>)
Determines whether one monetary value is less than or equal to another in the same currency.
public static bool operator <=(Money<TCurrency> left, Money<TCurrency> right)
Parameters
Returns
operator *(Money<TCurrency>, decimal)
Multiplies a monetary value by a scalar, rounding the result to the currency's minor-unit precision using banker's rounding (ToEven).
public static Money<TCurrency> operator *(Money<TCurrency> left, decimal right)
Parameters
Returns
- Money<TCurrency>
The product, rounded to
TCurrency.MinorUnitsusing banker's rounding.
Remarks
Every scalar multiplication rounds at the call site, so a chain of multiplications can accumulate rounding error. For chains where the intermediate precision matters (compound interest, tax stacking, rate-conversion percentages), perform the calculation through ToFraction() and snap to Money<TCurrency> only once, at the end, via FromFraction(Fraction<BigInteger>, MidpointRounding) or the MultiplyExact(Fraction<BigInteger>, MidpointRounding) shortcut. To control the rounding rule on a single scalar multiplication, use Multiply(decimal, MidpointRounding) in place of the operator.
Exceptions
- OverflowException
The product falls outside the range of decimal.
operator *(decimal, Money<TCurrency>)
Multiplies a scalar by a monetary value, rounding the result to the currency's minor-unit precision using banker's rounding (ToEven).
public static Money<TCurrency> operator *(decimal left, Money<TCurrency> right)
Parameters
Returns
- Money<TCurrency>
The product, rounded to
TCurrency.MinorUnitsusing banker's rounding.
Exceptions
- OverflowException
The product falls outside the range of decimal.
operator -(Money<TCurrency>, Money<TCurrency>)
Subtracts one monetary value from another, where both are denominated in the same currency.
public static Money<TCurrency> operator -(Money<TCurrency> left, Money<TCurrency> right)
Parameters
Returns
- Money<TCurrency>
The difference
left - right.
Exceptions
- OverflowException
The difference falls outside the range of decimal.
operator -(Money<TCurrency>)
Negates a monetary value.
public static Money<TCurrency> operator -(Money<TCurrency> value)
Parameters
valueMoney<TCurrency>The amount to negate.
Returns
- Money<TCurrency>
A Money<TCurrency> whose amount is the negation of
value.
Exceptions
- OverflowException
The negated value falls outside the range of decimal.
operator +(Money<TCurrency>)
Returns the operand unchanged.
public static Money<TCurrency> operator +(Money<TCurrency> value)
Parameters
valueMoney<TCurrency>The amount.
Returns
- Money<TCurrency>
The unchanged
value.
Explicit Interface Implementations
Parse(ReadOnlySpan<char>, IFormatProvider?)
Parses a monetary value from its span representation.
static Money<TCurrency> Parse(ReadOnlySpan<char> s, IFormatProvider? provider)
Parameters
sReadOnlySpan<char>The span to parse.
providerIFormatProviderThe culture used to parse the numeric component.
Returns
- Money<TCurrency>
The parsed amount.
Exceptions
- FormatException
Thrown when
sis not a valid representation.
Parse(string, IFormatProvider?)
Parses a monetary value from its string representation.
static Money<TCurrency> Parse(string s, IFormatProvider? provider)
Parameters
sstringThe string to parse. See TryParse(ReadOnlySpan<char>, IFormatProvider?, out Money<TCurrency>) for accepted forms.
providerIFormatProviderThe culture used to parse the numeric component.
Returns
- Money<TCurrency>
The parsed amount.
Exceptions
- ArgumentNullException
Thrown when
sis null.- FormatException
Thrown when
sis not a valid representation.
TryParse(ReadOnlySpan<char>, IFormatProvider?, out Money<TCurrency>)
Attempts to parse a monetary value from its span representation.
static bool TryParse(ReadOnlySpan<char> s, IFormatProvider? provider, out Money<TCurrency> result)
Parameters
sReadOnlySpan<char>The span to parse. Accepted forms are a bare decimal (
"19.99"), the ISO code followed by a decimal ("USD 19.99"), or a decimal followed by the ISO code ("19.99 USD"). When an ISO code is present it must matchTCurrency.IsoCode exactly, including case. Currency symbols such as$are not accepted because they are ambiguous across currencies.providerIFormatProviderThe culture used to parse the numeric component.
resultMoney<TCurrency>When this method returns true, the parsed amount; otherwise the default value.
Returns
Examples
using System.Globalization;
using Bodu.Financial;
using Bodu.Financial.Currencies;
// A bare decimal, or the ISO code at either end, all parse to the same value.
var a = Money<USD>.Parse("19.99", CultureInfo.InvariantCulture);
var b = Money<USD>.Parse("USD 19.99", CultureInfo.InvariantCulture);
// TryParse rejects a mismatched ISO code instead of throwing.
bool ok = Money<USD>.TryParse("EUR 19.99", CultureInfo.InvariantCulture, out _); // false
TryParse(string?, IFormatProvider?, out Money<TCurrency>)
Attempts to parse a monetary value from its string representation.
static bool TryParse(string? s, IFormatProvider? provider, out Money<TCurrency> result)
Parameters
sstringThe string to parse.
providerIFormatProviderThe culture used to parse the numeric component.
resultMoney<TCurrency>When this method returns true, the parsed amount; otherwise the default value.
Returns
Applies to
| Product | Versions |
|---|---|
| .NET | 8, 10 |