ddn.num.decimal

ddn.num.decimal — IEEE 754 decimal floating-point values.

Decimal64 and Decimal128: value semantics, exact text, total ordering and BID interchange for the two IEEE decimal widths. Arithmetic (contexts, rounding, statuses) lands with M037; this module ships the value layer of the design record doc/DDN-DECIMAL-IEEE.md (D1–D6, D8, D10–D12).

Semantics highlights:

  • Value identity: 1.0, 1.00 and 10E-1 are one value —

    equality and hashing are numerical (trailing zeros and the signedness of zero do not matter); the quantum (exponent of the least significant digit) is preserved as data.

  • BID bits are an interchange encoding, not the identity:

    fromBits decodes any well-formed pattern to its value and applies the standard's treat-as-zero rule to non-canonical finites; bitsOf writes the canonical pattern (it cannot fail — values are canonical by construction).

  • compareTotal is IEEE totalOrder: signed zeros, quantum

    and NaN sign/kind/payload all order.

  • Parse/format are exact; parse failures are Result errors

    (an input boundary), never IEEE statuses.

Example:

import ddn.num.decimal;

auto a = Decimal64.fromString("1.0");
auto b = Decimal64.fromString("10E-1");
assert(a.ok && b.ok && a.value == b.value);
assert(a.value.toString() == "1.0"); // quantum kept
assert(Decimal64.fromBits(0x31C0000000000001uL).coefficient == 1);

Types 10

Value class (parallels ddn.encoding.bid.BidClass).

FINITEA finite value (always canonical here: coefficient < 10^p).
INFINITY±Infinity.
NAN_QUIETQuiet NaN (payload preserved).
NAN_SIGNALINGSignaling NaN (payload preserved; arithmetic quiets it).

IEEE rounding modes (M037 consumes these; defined with the value layer so contexts can exist independently).

HALF_EVENRound to nearest, ties to even (the default).
HALF_UPRound to nearest, ties away from zero.
HALF_DOWNRound to nearest, ties toward zero.
CEILINGToward +infinity.
FLOORToward −infinity.
UPAway from zero.
DOWNToward zero.
ZERO_FIVE_UPRound away from zero only when truncation would leave the final digit 0 or 5 and the tail is nonzero (the `05up` mode).
enumDecimalStatus : uint

Sticky IEEE status flags (bitmask).

INVALID_OPERATION = 1u << 0Invalid operation (sNaN consumed, 0/0, inf−inf, ...).
DIVISION_BY_ZERO = 1u << 1Division by zero (finite/zero).
OVERFLOW = 1u << 2Overflow after rounding.
UNDERFLOW = 1u << 3Underflow (subnormal result, possibly inexact).
INEXACT = 1u << 4Rounded or discarded nonzero tail.
ROUNDED = 1u << 5Result rounded though possibly still exact.
SUBNORMAL = 1u << 6Result (or its exact intermediate) is subnormal — below Emin.
CLAMPED = 1u << 7Exponent altered to fit the format (Emax or Etiny side).

Explicit arithmetic context — never global state (D4). M037's operations consume it; value-layer code only carries it.

Fields
ushort precisionWorking precision in digits (0 = format precision).
Rounding roundingRounding direction.
uint flagsSticky status flags (accumulated by operations).
uint trapsEnabled traps (set the status and return the default result).
Methods
DecimalContext make() static @safe pure nothrow @nogcThe default context: format precision, HALF_EVEN, no flags.
classDecimalException : Exception

Programming-error exception (never an IEEE condition — those are statuses; never a parse failure — those are Results).

Constructors
this(string msg)Params: msg = description of the contract violation.

Recoverable text-parse failure with bounded context.

Fields
Code codeWhich failure happened.
size_t offsetOffset in the input where the failure was detected.
Nested Templates
CodeFailure classification.
structDecimal(int P, ushort Bias, ushort MaxE, C)

IEEE decimal floating-point value (M036 layer: construction, classification, equality/hash/ordering, text and BID interchange).

Parameters

Pcoefficient precision in decimal digits (16 / 34).
Biasexponent bias (398 / 6176).
MaxElargest canonical biased exponent (767 / 12287).
Ccoefficient storage (ulong / std.int128.Int128).
Fields
int precisionCoefficient digit precision.
ushort biasExponent bias.
ushort maxBiasedExponentLargest canonical biased exponent.
private Bits _bits
private int emax
Methods
private Bits _signBit() static @safe pure nothrow @nogc
private Bits _packFinite(bool sign, C coefficient, ushort biasedExponent) static @safe pure nothrow @nogc
private Bits _packInfinity(bool sign) static @safe pure nothrow @nogc
private Bits _packNan(bool sign, bool signaling, C payload) static @safe pure nothrow @nogc
private bool _decodeSign(Bits bits) static @safe pure nothrow @nogc
private DecimalClass _decodeCls(Bits bits) static @safe pure nothrow @nogc
private C _decodeCoefficient(Bits bits) static @safe pure nothrow @nogc
private ushort _decodeExponent(Bits bits) static @safe pure nothrow @nogc
private C _decodePayload(Bits bits) static @safe pure nothrow @nogc
private bool _sign() const @safe pure nothrow @nogc
private DecimalClass _cls() const @safe pure nothrow @nogc
private C _coefficient() const @safe pure nothrow @nogc
private ushort _biasedExponent() const @safe pure nothrow @nogc
private C _payload() const @safe pure nothrow @nogc
Decimal zero() static @safe pure nothrow @nogcPositive zero (quantum 0).
Decimal infinity(bool sign = false) static @safe pure nothrow @nogcInfinity. Params: sign = negative infinity when `true`.
Decimal nan(bool signaling = false, C payload = C.init) static @safe pure nothrow @nogcNaN with payload (`payload < 2^57` / `2^122`). Params: signaling = signaling NaN when `true`. payload = NaN payload bits (kept verbatim).
DecimalClass cls() @property const @safe pure nothrow @nogcThe value class.
bool sign() @property const @safe pure nothrow @nogcSign bit (finites and infinities; preserved data for NaNs).
C coefficient() @property const @safe pure nothrow @nogcCoefficient (`< 10^P`); zero for specials.
ushort biasedExponent() @property const @safe pure nothrow @nogcBiased exponent (finites only).
int quantum() @property const @safe pure nothrow @nogcUnbiased exponent of the least significant digit (the quantum); finites only.
int adjustedExponent() @property const @safe pure nothrow @nogcExponent of the leading digit (adjusted exponent), finites.
bool isFinite() const @safe pure nothrow @nogcTrue for finite values (canonical by construction).
bool isInfinite() const @safe pure nothrow @nogcTrue for ±Infinity.
bool isNaN() const @safe pure nothrow @nogcTrue for NaN of either kind.
bool isSignaling() const @safe pure nothrow @nogcTrue for signaling NaN.
bool isSigned() const @safe pure nothrow @nogcTrue for a negative value (sign bit set, specials included).
bool isZero() const @safe pure nothrow @nogcTrue for ±0.
bool isSubnormal() const @safe pure nothrow @nogcTrue for nonzero finites below the normal magnitude range (adjusted exponent below `emax-(P-1)`, the IEEE decimal subnormal class).
C nanPayload() @property const @safe pure nothrow @nogcNaN payload bits (both NaN kinds).
bool opEquals(const typeof(this) rhs) const @safe pure nothrow @nogcNumerical equality: trailing zeros, ±0 and NaN per IEEE (NaN unequal to everything, including itself).
size_t toHash() const @safe pure nothrow @nogcHash consistent with numerical equality.
int opCmp(const typeof(this) rhs) const @safe pure nothrow @nogcNumerical ordering with NaN above everything (a total extension for sorting; use compareTotal for IEEE `totalOrder`).
int compareTotal(const typeof(this) rhs) const @safe pure nothrow @nogcIEEE `totalOrder`: signed zeros (−0 < +0), quantum (1.0 < 1.00) and NaN sign/kind/payload all participate.
private Stripped _stripped() const @safe pure nothrow @nogc
private bool _sameMagnitude( const typeof(this) rhs) const @safe pure nothrow @nogc
private int _cmpMagnitude( const typeof(this) rhs) const @safe pure nothrow @nogc
private int _digits( C v) static @safe pure nothrow @nogc
string toString() const @safe pureExact rendering preserving the quantum: plain decimal when the adjusted exponent is ≥ −6 (`0.0001`), otherwise scientific (`1E+7`, `1.5E-9`); specials as `Inf`, `-Inf`, `NaN`, `sNaN` (payload r...
Result!( typeof(this), DecimalParseError) fromString( scope const( char)[] text) static @safe pureExact parse. Grammar: `[+-]? digits [ '.' digits ] [ (e|E) [+-]? digits ]`, or `Inf`, `Infinity`, `NaN[ddddd]`, `sNaN[ddddd]` (with optional sign on NaN per IEEE 754-2019).
typeof( this) fromStringRounded( scope const( char)[] text, ref DecimalContext ctx) static @safe pureContext-applied parse (`create_decimal` semantics): the text is converted under the context — coefficients longer than the precision round, ideal exponents beyond the format clamp (with `Clamped`...
private bool _ciEqual( scope const( char)[] a, scope const( char)[] b) static @safe pure nothrow @nogc
private bool _ciStartsWith( scope const( char)[] a, scope const( char)[] prefix) static @safe pure nothrow @nogc
private bool _startsWith( scope const( char)[] s, scope const( char)[] prefix) static @safe pure nothrow @nogc
Bits bitsOf() const @safe pure nothrow @nogcThe canonical BID bit pattern (cannot fail: values are canonical by construction).
Decimal fromBits( Bits bits) static @safe pure nothrow @nogcDecodes any well-formed BID pattern to its value. Non-canonical finites (coefficient ≥ 10^P in either form) follow the standard's input rule: the significand is treated as zero (sign and exponent...
private typeof( this) _nanPropagate( const ref typeof( this) a, const ref typeof( this) b, ref DecimalContext ctx) static @safe pure nothrow @nogc
typeof(this) add( const typeof( this) rhs, ref DecimalContext ctx) const @safe pure nothrow @nogcIEEE addition under an explicit context (statuses sticky).
typeof(this) plusOp(ref DecimalContext ctx) const @safe pure nothrow @nogcUnary plus under an explicit context (`decNumber plus`): `0 + x` — the value rounded to the context precision with the operand's preferred exponent. NaNs: signaling quieten (`INVALID_OPERATION`),...
typeof(this) minusOp(ref DecimalContext ctx) const @safe pure nothrow @nogcUnary minus under an explicit context (`decNumber minus`): `0 - x` — negation rounded to the context precision (the operand's preferred exponent). Signaling NaNs signal and quieten (sign unchange...
typeof(this) abs(ref DecimalContext ctx) const @safe pure nothrow @nogcThe absolute value under an explicit context (`decNumber abs`): sign cleared for numbers and infinities; NaNs return with sign and payload untouched (signaling quieten with `INVALID_OPERATION`). No...
typeof(this) reduce(ref DecimalContext ctx) const @safe pure nothrow @nogcThe value with trailing coefficient zeros removed (`decNumber reduce`): `1.00` → `1`, `10.0` → `1E+1`; zeros keep their sign and collapse to exponent 0; specials return unchanged except that si...
typeof(this) logB(ref DecimalContext ctx) const @safe pure nothrow @nogcThe adjusted exponent of the leading digit as a decimal value (`decNumber logb`): `0.03` → `-2`, `1` → `0`. Zero signals `DIVISION_BY_ZERO` with a −∞ result; ±∞ gives +∞; NaNs follow t...
typeof(this) copy() const @safe pure nothrow @nogcAn independent copy (this value type is trivially copyable).
typeof(this) copyAbs() const @safe pure nothrow @nogcThe absolute value without context and without NaN quieting (copy-family rule: signs clear for every class, NaN kind and payload untouched, never signals).
typeof(this) copyNegate() const @safe pure nothrow @nogcNegation without context and without NaN quieting (copy-family rule: payload and kind untouched).
typeof(this) copySign(const typeof(this) rhs) const @safe pure nothrow @nogcCopies the sign of `rhs` onto this value (copy-family rule: magnitude, quantum, NaN kind and payload untouched; the signaling NaN operand never signals).
typeof(this) compareOp( const typeof( this) rhs, ref DecimalContext ctx) const @safe pure nothrow @nogcIEEE `compare` under an explicit context: returns -1, 0 or 1 as a decimal value, or a quiet NaN carrying the left operand's sign when either operand is NaN (signaling NaNs signal `INVALID_OPERATION...
typeof(this) quantize( const typeof( this) rhs, ref DecimalContext ctx) const @safe pure nothrow @nogcIEEE `quantize`: the result has the same exponent as `rhs` (its quantum), with the coefficient of `this` rounded there under the context. `Underflow` is suppressed per the specification. Signals `I...
bool sameQuantum( const typeof( this) rhs) const @safe pure nothrow @nogcIEEE `sameQuantum`: `true` when both operands are finite with the same stored exponent, both are infinities (either sign), or both are NaNs of any kind — payloads and signs do not matter. Exactly...
private typeof(this) _quieted(ref DecimalContext ctx) const @safe pure nothrow @nogc
int compareTotalMag(const typeof(this) rhs) const @safe pure nothrow @nogcIEEE `totalOrderMag`: `compareTotal` on the magnitude views (signs cleared; zeros keep their quantum ordering, NaN payloads order as in `compareTotal`).
Class valueClass() const @safe pure nothrow @nogcThe class of this value (`decNumber class`).
private typeof( this) _nanInvalid( ref DecimalContext ctx) const @safe pure nothrow @nogc
typeof(this) minOp( const typeof( this) rhs, ref DecimalContext ctx) const @safe pure nothrow @nogcIEEE `min`: the smaller operand under numerical comparison. A quiet-NaN operand means the NaN is ignored (the other operand wins); signaling NaNs signal `INVALID_OPERATION` with a quiet-NaN result....
typeof(this) maxOp( const typeof( this) rhs, ref DecimalContext ctx) const @safe pure nothrow @nogcIEEE `max`: the larger operand (see minOp).
typeof(this) minMagOp( const typeof( this) rhs, ref DecimalContext ctx) const @safe pure nothrow @nogcIEEE `minMag`: smaller by magnitude, ties by value.
typeof(this) maxMagOp( const typeof( this) rhs, ref DecimalContext ctx) const @safe pure nothrow @nogcIEEE `maxMag`: larger by magnitude, ties by value.
private typeof( this) _minMax( bool wantMin, bool byMag)( const typeof( this) rhs, ref DecimalContext ctx) const @safe pure nothrow @nogc
private Decimal _strippedSignCleared() const @safe pure nothrow @nogc
private int _cmpAbs( const typeof( this) rhs) const @safe pure nothrow @nogc
typeof(this) fma( const typeof( this) rhs, const typeof( this) addend, ref DecimalContext ctx) const @safe pure nothrow @nogcIEEE fused multiply-add: `this * rhs + addend` computed exactly and rounded once under the context. The ideal exponent of an exact result is the finer of the product's and the addend's.
private typeof( this) _nanPropagate3( const ref typeof( this) x, const ref typeof( this) y, const ref typeof( this) z, ref DecimalContext ctx) static @safe pure nothrow @nogc
private void _alignCoeff( ref DigBuf m, int fromE, int toE, ref bool sticky) static @safe pure nothrow @nogc
typeof(this) remainder( const typeof( this) rhs, ref DecimalContext ctx) const @safe pure nothrow @nogcIEEE `remainder`: `this - n * rhs` with `n` the truncated integer quotient (fmod semantics; the sign follows `this`). Exact (no rounding flags); the result keeps the ideal exponent of the reduction...
private typeof( this) _finishClamped( typeof( this) v, int re, ref DecimalContext ctx) const @safe pure nothrow @nogc
private typeof( this) _checkedResult( typeof( this) v, ref DecimalContext ctx) const @safe pure nothrow @nogc
typeof(this) sub( const typeof( this) rhs, ref DecimalContext ctx) const @safe pure nothrow @nogcIEEE subtraction under an explicit context.
typeof(this) mul( const typeof( this) rhs, ref DecimalContext ctx) const @safe pure nothrow @nogcIEEE multiplication under an explicit context.
private bool r_sign( bool a, bool b) static @safe pure nothrow @nogc
typeof(this) div( const typeof( this) rhs, ref DecimalContext ctx) const @safe pure nothrow @nogcIEEE division under an explicit context.
private Decimal _zeroResult( bool sign, int exp, ref DecimalContext ctx) const @safe pure nothrow @nogc
private int _clampExp( int e) static @safe pure nothrow @nogc
Decimal fromIntegral( long v, ref DecimalContext ctx) static @safe pure nothrow @nogcConstruction from a signed integer under a context: values beyond the format's digits round (statuses `ROUNDED`/`INEXACT`).
Decimal fromIntegral( ulong v, ref DecimalContext ctx) static @safe pure nothrow @nogcDitto (unsigned).
long toLong( ref DecimalContext ctx) const @safe pure nothrow @nogcInteger conversion with context rounding: fractional parts round per the mode (`ROUNDED`/`INEXACT`); NaN, infinity and magnitudes beyond `long` set `INVALID_OPERATION` and return 0 (the IEEE defaul...
private Decimal _fromFreeText( scope const( char)[] text, ref DecimalContext ctx) static @safe
Decimal fromFixedDecimal( I, int Scale)( const FixedDecimal!( I, Scale) fd, ref DecimalContext ctx) static @safeConstruction from any `FixedDecimal` form via its exact text (18/38-digit fixed versus 16/34-digit IEEE boundaries resolve through the context; no fixed-arithmetic coupling).
FixedDecimal!long toFixedDecimal( ref DecimalContext ctx) const @safe pureChecked conversion to a runtime-scale `FixedDecimal!long` (18 digits, scale 0..18): quantum beyond the fixed range or coefficients beyond 18 digits round under the context.
Decimal fromDouble( double v, ref DecimalContext ctx) static @safeOpt-in binary-float bridge: renders the double with 17 significant digits (the round-trip guarantee) and rounds under the context. Exact doubles stay exact when their digits fit. Allocates (text pa...
double toDouble( ref DecimalContext ctx) const @safe`toDouble` with exactness tracking: sets `INEXACT`/`ROUNDED` when the value differs from its 17-significant-digit form (the shortest form guaranteed to round-trip a double), and `OVERFLOW`/`INEXACT...
double toDouble() const @safe pureDecimal to double via text (the runtime parser's rounding; exactness of the binary side is not tracked — a documented, status-free bridge). Specials map to their double forms.
private Decimal _finishStatic( DigBuf m, int e, int idealE, bool neg, bool stickyTail, bool exactBelow, ref DecimalContext ctx) static @safe pure nothrow @nogc
private bool _roundUpDecide( int guard, bool tail, bool lastKeptOdd, bool neg, Rounding r, bool m05UpLastKept = false) static @safe pure nothrow @nogc
private void _aligned( const ref typeof( this) v, int target, ref DigBuf out_, ref bool sticky) static @safe pure nothrow @nogc
private Decimal _finish( DigBuf m, int e, int idealE, bool neg, bool stickyTail, bool exactBelow, ref DecimalContext ctx) const @safe pure nothrow @nogc
private C _narrow( Int128 v) static @safe pure nothrow @nogc
private Decimal64 dec( string s) static @safe
Constructors
this(Bits bits)
this(bool sign, C coefficient, ushort biasedExponent)Checked finite construction.
Nested Templates
Stripped
ClassThe ten IEEE 754 / decNumber value classes.
DigBuf
aliasDecimal32 = Decimal!( 7, 101, 191, ulong)

IEEE decimal32 (7 digits, bias 101).

aliasDecimal64 = Decimal!( 16, 398, 767, ulong)

IEEE decimal64 (16 digits, bias 398).

aliasDecimal128 = Decimal!( 34, 6176, 12287, Int128)

IEEE decimal128 (34 digits, bias 6176).

Functions 8

private fnstring _digitsText(C)(C v) @safe pure
private fnint coCoefficientDigits(C)(C v) @safe pure nothrow @nogc
private fnC _pow10(C)(int n) @safe pure nothrow @nogc
private fnC _maxCoefficient(C)(int p) @safe pure nothrow @nogc
private fnint _digitCount(ulong v) @safe pure nothrow @nogc
private fnint _digitCount128(Int128 v) @safe pure nothrow @nogc
private fnint _trailingZeros(ulong v) @safe pure nothrow @nogc
private fnint _trailingZeros128(Int128 v) @safe pure nothrow @nogc