Table of Contents

Struct DecimalAmount

Namespace
Virtufin.Core.Position
Assembly
Virtufin.Core.dll

Fixed-point decimal amount with base 10, stored as a whole part, a fractional part, and a precision (number of fractional digits). Backed by long for the whole part and ulong for the fractional part so values up to roughly 10^18 are representable without floating-point error.

public readonly record struct DecimalAmount : IFixedPointAmount<DecimalAmount>, IFixedPointAmount, IComparable<DecimalAmount>, IAsCardinal<long>, IAmount<(long, ulong)>, IAmount, IEquatable<IAmount<(long, ulong)>>, IComparable<IAmount<(long, ulong)>>, IAdditionOperators<DecimalAmount, DecimalAmount, DecimalAmount>, ISubtractionOperators<DecimalAmount, DecimalAmount, DecimalAmount>, IMultiplyOperators<DecimalAmount, DecimalAmount, DecimalAmount>, IDivisionOperators<DecimalAmount, DecimalAmount, DecimalAmount>, IEquatable<DecimalAmount>
Implements
Inherited Members

Examples

var a = new DecimalAmount(1, 23, 2); // 1.23
var b = new DecimalAmount(0, 50, 2); // 0.50
var sum = a + b;                     // 1.73
Console.WriteLine(sum);              // "1.73"

Remarks

Addition and subtraction use checked semantics and throw OverflowException on overflow. Subtraction is allowed to go negative; the caller decides whether to throw on negative results.

Equality and ordering both include the precision: an amount at a different N is a different value. The invariant cannot be enforced statically (C# lacks dependent types), so it is enforced at runtime -- an amount at precision 2 and one at precision 4 are, in effect, different types.

They report a mismatch differently, and deliberately so, following the BCL's own split for a type mismatch: equality returns false (as Equals(object) does when handed another type), while ordering throws PrecisionMismatchException (as CompareTo(object) throws ArgumentException). Equality must stay total because dictionaries, Contains, Distinct and the generated equality of any record holding an amount all call it where a throw cannot be caught. Arithmetic keeps throwing: unlike a comparison, 5.00 + 5.0000 has no answer until someone picks a precision.

Multiplication and division are delegated to FixedPointArithmetic: the product is expressed at the summed precision, the quotient at the dividend's precision.

Constructors

DecimalAmount(long, ulong, int)

Fixed-point decimal amount with base 10, stored as a whole part, a fractional part, and a precision (number of fractional digits). Backed by long for the whole part and ulong for the fractional part so values up to roughly 10^18 are representable without floating-point error.

public DecimalAmount(long Whole, ulong Fraction, int N)

Parameters

Whole long
Fraction ulong
N int

Examples

var a = new DecimalAmount(1, 23, 2); // 1.23
var b = new DecimalAmount(0, 50, 2); // 0.50
var sum = a + b;                     // 1.73
Console.WriteLine(sum);              // "1.73"

Remarks

Addition and subtraction use checked semantics and throw OverflowException on overflow. Subtraction is allowed to go negative; the caller decides whether to throw on negative results.

Equality and ordering both include the precision: an amount at a different N is a different value. The invariant cannot be enforced statically (C# lacks dependent types), so it is enforced at runtime -- an amount at precision 2 and one at precision 4 are, in effect, different types.

They report a mismatch differently, and deliberately so, following the BCL's own split for a type mismatch: equality returns false (as Equals(object) does when handed another type), while ordering throws PrecisionMismatchException (as CompareTo(object) throws ArgumentException). Equality must stay total because dictionaries, Contains, Distinct and the generated equality of any record holding an amount all call it where a throw cannot be caught. Arithmetic keeps throwing: unlike a comparison, 5.00 + 5.0000 has no answer until someone picks a precision.

Multiplication and division are delegated to FixedPointArithmetic: the product is expressed at the summed precision, the quotient at the dividend's precision.

Fields

DefaultPrecision

Default precision (fractional digits) for newly constructed amounts when no precision is supplied. Matches conventional currency representation.

public const int DefaultPrecision = 2

Field Value

int

MaxPrecision

Largest precision a DecimalAmount can express: 10^18 fits in a long, 10^19 does not.

public const int MaxPrecision = 18

Field Value

int

Radix

Radix of the fractional representation.

public const int Radix = 10

Field Value

int

Properties

Base

Radix of the fractional representation (e.g. 10 or 2). Always >= 2.

public int Base { get; }

Property Value

int

BaseValue

The type's fixed base, usable in generic code without an instance. Mirrors the instance Base.

public static int BaseValue { get; }

Property Value

int

Fraction

The fractional part, a magnitude in [0, Base^Precision).

public ulong Fraction { get; init; }

Property Value

ulong

IsZero

true when this amount equals the additive identity.

public bool IsZero { get; }

Property Value

bool

N

public int N { get; init; }

Property Value

int

Precision

Number of fractional digits carried by this amount.

public int Precision { get; }

Property Value

int

Whole

The whole (integral) part.

public long Whole { get; init; }

Property Value

long

Zero

A zero amount at DefaultPrecision precision.

public static DecimalAmount Zero { get; }

Property Value

DecimalAmount

Methods

CompareTo(DecimalAmount)

Compares the current instance with another object of the same type and returns an integer that indicates whether the current instance precedes, follows, or occurs in the same position in the sort order as the other object.

public int CompareTo(DecimalAmount other)

Parameters

other DecimalAmount

An object to compare with this instance.

Returns

int

A value that indicates the relative order of the objects being compared. The return value has these meanings:

Value Meaning
Less than zero This instance precedes other in the sort order.
Zero This instance occurs in the same position in the sort order as other.
Greater than zero This instance follows other in the sort order.

Exceptions

PrecisionMismatchException

Precisions differ.

CompareTo(IAmount)

Compare with another amount. Implementations reject differing scales (base/precision) at runtime via PrecisionMismatchException.

public int CompareTo(IAmount other)

Parameters

other IAmount

Amount to compare against.

Returns

int

Create(long, ulong, int)

Construct an instance from decomposed parts at the given precision.

public static DecimalAmount Create(long whole, ulong fraction, int precision)

Parameters

whole long

Whole part.

fraction ulong

Fractional magnitude in [0, Base^precision).

precision int

Number of fractional digits.

Returns

DecimalAmount

Equals(DecimalAmount)

Indicates whether the current object is equal to another object of the same type.

public bool Equals(DecimalAmount other)

Parameters

other DecimalAmount

An object to compare with this object.

Returns

bool

true if the current object is equal to the other parameter; otherwise, false.

Remarks

Precision is part of identity: an amount at a different N is a different value, never an equal one. It returns false rather than throwing, unlike CompareTo(DecimalAmount) -- that asymmetry is deliberate and mirrors the BCL, where a type mismatch makes Equals(object) return false but CompareTo(object) throw. Equality is called implicitly by dictionaries, Contains, Distinct and by the generated equality of any record holding an amount, where a throw cannot be caught.

From(decimal, int)

Construct a DecimalAmount from a decimal. This is the exact constructor — no double round-trip.

public static DecimalAmount From(decimal value, int n)

Parameters

value decimal

Source value.

n int

Precision (number of fractional digits).

Returns

DecimalAmount

From(double, int)

Construct a DecimalAmount from a double. Prefer From(decimal, int) for exact values; this overload exists for call sites that already hold a double.

public static DecimalAmount From(double value, int n)

Parameters

value double

Source value.

n int

Precision (number of fractional digits).

Returns

DecimalAmount

FromCardinal(long)

Recover whole and fractional parts from a cardinal value at this amount's precision.

public (long Whole, ulong Fraction) FromCardinal(long cardinal)

Parameters

cardinal long

Cardinal value to decompose.

Returns

(long Whole, ulong Fraction)

FromCardinal(long, int)

Recover whole and fractional parts from a cardinal value at an explicit precision.

public static (long Whole, ulong Fraction) FromCardinal(long cardinal, int n)

Parameters

cardinal long

Cardinal value to decompose.

n int

Precision the cardinal is expressed at.

Returns

(long Whole, ulong Fraction)

GetHashCode()

Returns the hash code for this instance.

public override int GetHashCode()

Returns

int

A 32-bit signed integer that is the hash code for this instance.

GetValue()

public (long Whole, ulong Fraction) GetValue()

Returns

(long Whole, ulong Fraction)

ToCardinal()

Compute the cardinal (single-integer) representation at this amount's precision.

public long ToCardinal()

Returns

long

ToCardinal(int)

Compute the cardinal (single-integer) representation at an explicit precision. Rescales when n differs from N, rounding half away from zero.

public long ToCardinal(int n)

Parameters

n int

Target precision (fractional digits).

Returns

long

ToDecimal()

Convert to decimal exactly.

public decimal ToDecimal()

Returns

decimal

ToString()

Format the amount as a decimal string.

public override string ToString()

Returns

string

Operators

operator +(DecimalAmount, DecimalAmount)

Add two amounts. Throws OverflowException on overflow.

public static DecimalAmount operator +(DecimalAmount left, DecimalAmount right)

Parameters

left DecimalAmount
right DecimalAmount

Returns

DecimalAmount

operator /(DecimalAmount, DecimalAmount)

Divide two amounts. The result carries the dividend's precision, rounded half away from zero. Throws DivideByZeroException when the divisor is zero.

public static DecimalAmount operator /(DecimalAmount left, DecimalAmount right)

Parameters

left DecimalAmount
right DecimalAmount

Returns

DecimalAmount

operator *(DecimalAmount, DecimalAmount)

Multiply two amounts. The result carries the summed precision of the operands (exact in base 10). Throws OverflowException on overflow.

public static DecimalAmount operator *(DecimalAmount left, DecimalAmount right)

Parameters

left DecimalAmount
right DecimalAmount

Returns

DecimalAmount

operator -(DecimalAmount, DecimalAmount)

Subtract two amounts. Throws OverflowException on overflow.

public static DecimalAmount operator -(DecimalAmount left, DecimalAmount right)

Parameters

left DecimalAmount
right DecimalAmount

Returns

DecimalAmount