Table of Contents

Money<TCurrency> Struct

Definition

Namespace
Bodu.Financial
Assembly
Bodu.Financial.dll
Package
Bodu.Financial 1.0.0
Source
Money{T}.Allocation.cs

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

TCurrency

A 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>>
IParsable<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

amount decimal

The 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 TCurrency reports 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

amount decimal

The monetary amount in the major unit of TCurrency.

rounding MidpointRounding

The rule used to round midpoint values.

Exceptions

InvalidOperationException

Thrown when TCurrency reports 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.05m for CHF), or 0m when 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

DateOnly?

The demonetization date, or null when the currency is active or the date is unknown.

IsHistoric

Gets a value indicating whether TCurrency has been demonetized.

public static bool IsHistoric { get; }

Property Value

bool

true when the currency is a historic predecessor; otherwise false.

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 TCurrency reports 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 TCurrency reports invalid metadata.

SuccessorIsoCode

Gets the ISO 4217 alphabetic code of the currency that replaced TCurrency, when applicable.

public static string? SuccessorIsoCode { get; }

Property Value

string

The three-letter ISO code of the successor currency, or null when there is no defined successor.

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

parts int

The number of shares to allocate. Must be greater than zero.

Returns

Money<TCurrency>[]

An array of parts Money<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 parts is 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

ratios ReadOnlySpan<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 ratios is 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

other Money<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

obj object

The boxed value to compare against.

Returns

int

The result of CompareTo(Money<TCurrency>) when obj matches; the comparison rules of IComparable when obj is null.

Exceptions

ArgumentException

Thrown when obj is not a Money<TCurrency> over the same TCurrency.

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

exchangeRate ExchangeRate<TCurrency, TQuote>

A strongly-typed rate whose base must match TCurrency.

rounding MidpointRounding

The midpoint-rounding rule applied when narrowing to the target precision.

Returns

Money<TQuote>

The converted monetary amount in TQuote.

Type Parameters

TQuote

The 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

exchangeRate decimal

The exchange rate, expressed as units of TTarget per single unit of TCurrency. Must be strictly positive.

rounding MidpointRounding

The midpoint-rounding rule applied when narrowing to the target precision.

Returns

Money<TTarget>

The converted monetary amount in TTarget.

Type Parameters

TTarget

The 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 exchangeRate is 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 × exchangeRate falls 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

divisor decimal

The scalar divisor.

Returns

Money<TCurrency>

The quotient, rounded to TCurrency.MinorUnits using banker's rounding.

Exceptions

DivideByZeroException

divisor is 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

divisor decimal

The scalar divisor.

rounding MidpointRounding

The midpoint-rounding rule applied to the quotient.

Returns

Money<TCurrency>

The quotient, rounded to TCurrency.MinorUnits using rounding.

Exceptions

DivideByZeroException

divisor is 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

other Money<TCurrency>

The monetary value to compare against.

Returns

bool

true when both instances share TCurrency and represent the same rounded amount; otherwise false.

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

obj object

The object to compare against.

Returns

bool

true when obj is 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

value Fraction<BigInteger>

The exact amount.

rounding MidpointRounding

The 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

minorUnits long

The 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

multiplier decimal

The scalar multiplier.

Returns

Money<TCurrency>

The product, rounded to TCurrency.MinorUnits using 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

multiplier decimal

The scalar multiplier.

context MonetaryContext

The monetary context whose rounding strategy is applied at the settlement boundary.

Returns

Money<TCurrency>

The product, rounded to TCurrency.MinorUnits using context.

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

context is 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

multiplier decimal

The scalar multiplier.

rounding MidpointRounding

The midpoint-rounding rule applied to the product.

Returns

Money<TCurrency>

The product, rounded to TCurrency.MinorUnits using rounding.

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

factor Fraction<BigInteger>

The exact multiplier.

rounding MidpointRounding

The 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

other Money<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

other has 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

decimals int

The number of fractional digits to keep. Must be non-negative and not greater than the currency's MinorUnits.

rounding MidpointRounding

The 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 decimals is 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

rounding MidpointRounding

The 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

Money

A Money carrying the same amount and the ISO code derived from TCurrency.

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

format string

The 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 format is 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

format string

The format specifier.

formatProvider IFormatProvider

The culture used to render the numeric component.

Returns

string

The formatted representation.

Exceptions

FormatException

Thrown when format is 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

utf8Destination Span<byte>

The span that receives the formatted UTF-8 bytes.

bytesWritten int

When this method returns, contains the number of bytes written.

format ReadOnlySpan<char>

The format specifier.

provider IFormatProvider

The culture used to render the numeric component.

Returns

bool

true when utf8Destination was large enough; otherwise false.

Exceptions

FormatException

Thrown when format is 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

destination Span<char>

The span that receives the formatted characters.

charsWritten int

When this method returns, contains the number of characters written.

format ReadOnlySpan<char>

The format specifier.

provider IFormatProvider

The culture used to render the numeric component.

Returns

bool

true when destination was large enough; otherwise false.

Exceptions

FormatException

Thrown when format is 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

minorUnits long

On success, the amount in minor units; otherwise the default value.

Returns

bool

true when the conversion succeeded; otherwise false.

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

left Money<TCurrency>

The first amount.

right Money<TCurrency>

The second amount.

Returns

Money<TCurrency>

The sum of left and right.

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

left Money<TCurrency>

The dividend.

right Money<TCurrency>

The divisor.

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

right has 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

left Money<TCurrency>

The amount.

right decimal

The scalar divisor.

Returns

Money<TCurrency>

The quotient, rounded to TCurrency.MinorUnits using 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

right is 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

left Money<TCurrency>

The first value.

right Money<TCurrency>

The second value.

Returns

bool

true when the two are equal; otherwise false.

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

value Money

The runtime-tagged money.

Returns

Money<TCurrency>

The strongly-typed equivalent.

Exceptions

InvalidOperationException

value's Code does not match the currency of TCurrency.

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

left Money<TCurrency>

The first amount.

right Money<TCurrency>

The second amount.

Returns

bool

true when left is greater than right; otherwise false.

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

left Money<TCurrency>

The first amount.

right Money<TCurrency>

The second amount.

Returns

bool

true when left is greater than or equal to right; otherwise false.

implicit operator Money(Money<TCurrency>)

Implicitly converts a Money<TCurrency> to a runtime-tagged Money.

public static implicit operator Money(Money<TCurrency> value)

Parameters

value Money<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

left Money<TCurrency>

The first value.

right Money<TCurrency>

The second value.

Returns

bool

true when the two differ; otherwise false.

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

left Money<TCurrency>

The first amount.

right Money<TCurrency>

The second amount.

Returns

bool

true when left is less than right; otherwise false.

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

left Money<TCurrency>

The first amount.

right Money<TCurrency>

The second amount.

Returns

bool

true when left is less than or equal to right; otherwise false.

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

left Money<TCurrency>

The amount.

right decimal

The scalar multiplier.

Returns

Money<TCurrency>

The product, rounded to TCurrency.MinorUnits using 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

left decimal

The scalar multiplier.

right Money<TCurrency>

The amount.

Returns

Money<TCurrency>

The product, rounded to TCurrency.MinorUnits using 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

left Money<TCurrency>

The minuend.

right Money<TCurrency>

The subtrahend.

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

value Money<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

value Money<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

s ReadOnlySpan<char>

The span to parse.

provider IFormatProvider

The culture used to parse the numeric component.

Returns

Money<TCurrency>

The parsed amount.

Exceptions

FormatException

Thrown when s is not a valid representation.

Parse(string, IFormatProvider?)

Parses a monetary value from its string representation.

static Money<TCurrency> Parse(string s, IFormatProvider? provider)

Parameters

s string

The string to parse. See TryParse(ReadOnlySpan<char>, IFormatProvider?, out Money<TCurrency>) for accepted forms.

provider IFormatProvider

The culture used to parse the numeric component.

Returns

Money<TCurrency>

The parsed amount.

Exceptions

ArgumentNullException

Thrown when s is null.

FormatException

Thrown when s is 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

s ReadOnlySpan<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 match TCurrency.IsoCode exactly, including case. Currency symbols such as $ are not accepted because they are ambiguous across currencies.

provider IFormatProvider

The culture used to parse the numeric component.

result Money<TCurrency>

When this method returns true, the parsed amount; otherwise the default value.

Returns

bool

true when parsing succeeded; otherwise false.

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

s string

The string to parse.

provider IFormatProvider

The culture used to parse the numeric component.

result Money<TCurrency>

When this method returns true, the parsed amount; otherwise the default value.

Returns

bool

true when parsing succeeded; otherwise false.

Applies to

ProductVersions
.NET8, 10