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.00and10E-1are 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:
fromBitsdecodes any well-formed pattern to its value and applies the standard's treat-as-zero rule to non-canonical finites;bitsOfwrites the canonical pattern (it cannot fail — values are canonical by construction). compareTotalis IEEEtotalOrder: signed zeros, quantumand NaN sign/kind/payload all order.
- Parse/format are exact; parse failures are
Resulterrors(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).
IEEE rounding modes (M037 consumes these; defined with the value layer so contexts can exist independently).
Sticky IEEE status flags (bitmask).
Explicit arithmetic context — never global state (D4). M037's operations consume it; value-layer code only carries it.
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).DecimalContext make() static @safe pure nothrow @nogcThe default context: format precision, HALF_EVEN, no flags.Programming-error exception (never an IEEE condition — those are statuses; never a parse failure — those are Results).
this(string msg)Params: msg = description of the contract violation.Recoverable text-parse failure with bounded context.
Code codeWhich failure happened.size_t offsetOffset in the input where the failure was detected.CodeFailure classification.IEEE decimal floating-point value (M036 layer: construction, classification, equality/hash/ordering, text and BID interchange).
Parameters
P | coefficient precision in decimal digits (16 / 34). |
Bias | exponent bias (398 / 6176). |
MaxE | largest canonical biased exponent (767 / 12287). |
C | coefficient storage (ulong / std.int128.Int128). |
int precisionCoefficient digit precision.ushort biasExponent bias.ushort maxBiasedExponentLargest canonical biased exponent.private Bits _bitsprivate int emaxBits _packFinite(bool sign, C coefficient,
ushort biasedExponent) static @safe pure nothrow @nogcDecimal 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).bool sign() @property const @safe pure nothrow @nogcSign bit (finites and infinities; preserved data for NaNs).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 isSigned() const @safe pure nothrow @nogcTrue for a negative value (sign bit set, specials included).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).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).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.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`...bool _ciEqual(
scope const(
char)[] a,
scope const(
char)[] b) static @safe pure nothrow @nogcbool _ciStartsWith(
scope const(
char)[] a,
scope const(
char)[] prefix) static @safe pure nothrow @nogcbool _startsWith(
scope const(
char)[] s,
scope const(
char)[] prefix) static @safe pure nothrow @nogcBits 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...typeof(
this) _nanPropagate(
const ref typeof(
this) a,
const ref typeof(
this) b, ref DecimalContext ctx) static @safe pure nothrow @nogctypeof(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...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`).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.typeof(
this) _minMax(
bool wantMin, bool byMag)(
const typeof(
this) rhs, ref DecimalContext ctx) const @safe pure nothrow @nogctypeof(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.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 @nogcvoid _alignCoeff(
ref DigBuf m, int fromE, int toE,
ref bool sticky) static @safe pure nothrow @nogctypeof(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...typeof(
this) _finishClamped(
typeof(
this) v, int re,
ref DecimalContext ctx) const @safe pure nothrow @nogctypeof(
this) _checkedResult(
typeof(
this) v,
ref DecimalContext ctx) const @safe pure nothrow @nogctypeof(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.typeof(this) div(
const typeof(
this) rhs, ref DecimalContext ctx) const @safe pure nothrow @nogcIEEE division under an explicit context.Decimal _zeroResult(
bool sign, int exp,
ref DecimalContext ctx) const @safe pure nothrow @nogcDecimal 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...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.Decimal _finishStatic(
DigBuf m, int e, int idealE,
bool neg, bool stickyTail, bool exactBelow,
ref DecimalContext ctx) static @safe pure nothrow @nogcbool _roundUpDecide(
int guard, bool tail,
bool lastKeptOdd, bool neg, Rounding r,
bool m05UpLastKept = false) static @safe pure nothrow @nogcvoid _aligned(
const ref typeof(
this) v, int target,
ref DigBuf out_, ref bool sticky) static @safe pure nothrow @nogcDecimal _finish(
DigBuf m, int e, int idealE, bool neg,
bool stickyTail, bool exactBelow, ref DecimalContext ctx) const @safe pure nothrow @nogcthis(Bits bits)this(bool sign, C coefficient, ushort biasedExponent)Checked finite construction.StrippedClassThe ten IEEE 754 / decNumber value classes.DigBufIEEE decimal128 (34 digits, bias 6176).