decimal_gda_checked API

decimal_gda_checked provides GdaDecimalChecked, a pipeline over the General Decimal Arithmetic (GDA) operations of decimal_gda. It holds exactly one GdaOutcome[Decimal]: the current defined value, the next context (whose sticky status accumulates every signal raised so far), the signals raised by the latest operation, and, if a trap fired, the trapped signal. An operation on a completed state runs the GDA operation with the stored context; an operation on a trapped state does nothing. resume_defined() is the only way to continue after a trap. The package never produces an ArithmeticError. The tutorial walks through traps and recovery; the design page models the pipeline as a state monad with an absorbing trap and proves the sticky-status laws.

The examples list GDA flags with this helper:

///|
fn gda_flags(f : @decimal_gda.GdaFlags) -> String {
  let named = [
    ("inexact", f.inexact),
    ("rounded", f.rounded),
    ("invalid_operation", f.invalid_operation),
    ("division_by_zero", f.division_by_zero),
    ("overflow", f.overflow),
    ("underflow", f.underflow),
    ("conversion_syntax", f.conversion_syntax),
    ("invalid_context", f.invalid_context),
  ]
  [ for p in named if p.1 => p.0 ].join(",")
}

The state type

GdaDecimalChecked

GdaDecimalChecked wraps one GdaOutcome[@decimal_gda.Decimal].

pub struct GdaDecimalChecked {
  // private fields
}

The outcome is either Completed(value, next_context, raised) or Trapped(signal, value, next_context, raised); see GdaOutcome in the decimal_gda API.

Construction

GdaDecimalChecked::from_outcome

from_outcome(outcome) wraps the outcome of any decimal_gda operation.

pub fn GdaDecimalChecked::from_outcome(@decimal_gda.GdaOutcome[@decimal_gda.Decimal]) -> Self

A Trapped outcome gives a trapped pipeline.

GdaDecimalChecked::from_decimal

from_decimal(value, context) rounds a value into the context with the GDA apply operation (the plus-like conversion to the context).

pub fn GdaDecimalChecked::from_decimal(@decimal_gda.Decimal, @decimal_gda.GdaContext) -> Self

The signals of the rounding are raised, merged into the status and checked against the traps, so construction itself can trap.

GdaDecimalChecked::parse

parse(source, context) is the GDA to-number conversion of a string.

pub fn GdaDecimalChecked::parse(String, @decimal_gda.GdaContext) -> Self

An invalid string gives NaN with conversion_syntax; because GDA treats conversion syntax as an invalid-operation condition, a context that traps InvalidOperation (such as GdaContext::default()) traps it.

Observation

outcome, value, context, raised, status

These methods return the wrapped outcome and its components.

pub fn GdaDecimalChecked::outcome(Self) -> @decimal_gda.GdaOutcome[@decimal_gda.Decimal]
pub fn GdaDecimalChecked::value(Self) -> @decimal_gda.Decimal
pub fn GdaDecimalChecked::context(Self) -> @decimal_gda.GdaContext
pub fn GdaDecimalChecked::raised(Self) -> @decimal_gda.GdaFlags
pub fn GdaDecimalChecked::status(Self) -> @decimal_gda.GdaFlags

value() is the defined result, also when trapped. context() is the next context, including its updated status and its traps. raised() holds the signals of the latest operation only. status() is context().status(), the sticky union of everything raised since the context’s status was last cleared; whenever any invalid-operation condition (conversion_syntax, division_impossible, division_undefined, invalid_context) is raised, the status also gets invalid_operation.

is_trapped, trapped_signal

These methods report whether a trap fired and which signal it was.

pub fn GdaDecimalChecked::is_trapped(Self) -> Bool
pub fn GdaDecimalChecked::trapped_signal(Self) -> @decimal_gda.GdaSignal?

When several raised signals are trapped at once, the reported one is the first in the priority order InvalidOperation, DivisionByZero, DivisionUndefined, DivisionImpossible, InvalidContext, ConversionSyntax, Overflow, Underflow, Subnormal, Inexact, Rounded, Clamped, LostDigits.

Recovery

GdaDecimalChecked::resume_defined

resume_defined() continues a trapped pipeline with its defined result.

pub fn GdaDecimalChecked::resume_defined(Self) -> Self

On Trapped(signal, value, context, raised) it returns Completed(value, context, GdaFlags::none()): the value and the context (with its status, which already contains the trapped signal) are kept, the trap marker and the latest-step flags are dropped. On a completed pipeline it does nothing. Traps stay enabled in the context, so the same condition traps again if it recurs.

///|
test "a trap stops the pipeline until it is resumed" {
  let ctx = @decimal_gda.GdaContext::new(precision=5).trap(
    @decimal_gda.GdaSignal::DivisionByZero,
  )
  let one = @decimal_gda.Decimal::one()
  let trapped = @decimal_gda_checked.GdaDecimalChecked::parse("1.234567", ctx).divide(
    @decimal_gda.Decimal::zero(),
  )
  inspect(trapped.is_trapped(), content="true")
  inspect(
    trapped.trapped_signal() == Some(@decimal_gda.GdaSignal::DivisionByZero),
    content="true",
  )
  inspect(trapped.value().to_string(), content="inf")
  inspect(gda_flags(trapped.raised()), content="division_by_zero")
  inspect(gda_flags(trapped.status()), content="inexact,rounded,division_by_zero")
  inspect(trapped.add(one).is_trapped(), content="true")
  let resumed = trapped.resume_defined()
  inspect(gda_flags(resumed.raised()), content="")
  let next = resumed.minus()
  inspect(next.value().to_string(), content="-inf")
  inspect(gda_flags(next.status()), content="inexact,rounded,division_by_zero")
}

Operations

apply, plus, minus, abs, add, subtract, multiply, divide, fma, sqrt, exp, ln, log10, power, quantize, remainder, reduce, next_minus, next_plus, next_toward

These methods apply the decimal_gda operation of the same name to the current value under the stored context.

pub fn GdaDecimalChecked::apply(Self) -> Self
pub fn GdaDecimalChecked::plus(Self) -> Self
pub fn GdaDecimalChecked::minus(Self) -> Self
pub fn GdaDecimalChecked::abs(Self) -> Self
pub fn GdaDecimalChecked::add(Self, @decimal_gda.Decimal) -> Self
pub fn GdaDecimalChecked::subtract(Self, @decimal_gda.Decimal) -> Self
pub fn GdaDecimalChecked::multiply(Self, @decimal_gda.Decimal) -> Self
pub fn GdaDecimalChecked::divide(Self, @decimal_gda.Decimal) -> Self
pub fn GdaDecimalChecked::fma(Self, @decimal_gda.Decimal, @decimal_gda.Decimal) -> Self
pub fn GdaDecimalChecked::sqrt(Self) -> Self
pub fn GdaDecimalChecked::exp(Self) -> Self
pub fn GdaDecimalChecked::ln(Self) -> Self
pub fn GdaDecimalChecked::log10(Self) -> Self
pub fn GdaDecimalChecked::power(Self, @decimal_gda.Decimal) -> Self
pub fn GdaDecimalChecked::quantize(Self, @decimal_gda.Decimal) -> Self
pub fn GdaDecimalChecked::remainder(Self, @decimal_gda.Decimal) -> Self
pub fn GdaDecimalChecked::reduce(Self) -> Self
pub fn GdaDecimalChecked::next_minus(Self) -> Self
pub fn GdaDecimalChecked::next_plus(Self) -> Self
pub fn GdaDecimalChecked::next_toward(Self, @decimal_gda.Decimal) -> Self

On Completed(v, c, _) the method returns the outcome of @decimal_gda.op(v, …, c); on Trapped it returns the state unchanged. The second operand (other, multiplier, addend, exponent, quantum, divisor, target) is a plain Decimal. The GDA operation computes the result under c’s precision, rounding, exponent limits, clamping and extended mode; if it raises no signal the context is passed on unchanged with empty raised; otherwise the raised signals are merged into the status of the next context and, if one of them is enabled in c.traps(), the outcome is Trapped. The mathematical functions exp, ln, log10 and power need precision and exponent limits within ±999 999\pm 999\,999; otherwise they return NaN with invalid_context.

///|
test "sticky status across operations" {
  let ctx = @decimal_gda.GdaContext::new(precision=5)
  let parsed = @decimal_gda_checked.GdaDecimalChecked::parse("1.234567", ctx)
  inspect(parsed.value().to_string(), content="1.2346")
  inspect(gda_flags(parsed.raised()), content="inexact,rounded")
  let added = parsed.add(@decimal_gda.Decimal::zero())
  inspect(gda_flags(added.raised()), content="")
  inspect(gda_flags(added.status()), content="inexact,rounded")
  let e = @decimal_gda_checked.GdaDecimalChecked::parse(
    "2",
    @decimal_gda.GdaContext::decimal64(),
  ).exp()
  inspect(e.value().to_string(), content="7.389056098930650")
  let q = parsed.quantize(@decimal_gda.Decimal::from_string("0.01").unwrap())
  inspect(q.value().to_string(), content="1.23")
}

Complete public interface

The following snapshot is the complete generated interface of the package.

// Generated using `moon info`, DON'T EDIT IT
package "Luna-Flow/floating/decimal_gda_checked"

import {
  "Luna-Flow/floating/decimal_gda",
}

// Values

// Errors

// Types and methods
pub struct GdaDecimalChecked {
  // private fields
}
pub fn GdaDecimalChecked::abs(Self) -> Self
pub fn GdaDecimalChecked::add(Self, @decimal_gda.Decimal) -> Self
pub fn GdaDecimalChecked::apply(Self) -> Self
pub fn GdaDecimalChecked::context(Self) -> @decimal_gda.GdaContext
pub fn GdaDecimalChecked::divide(Self, @decimal_gda.Decimal) -> Self
pub fn GdaDecimalChecked::exp(Self) -> Self
pub fn GdaDecimalChecked::fma(Self, @decimal_gda.Decimal, @decimal_gda.Decimal) -> Self
pub fn GdaDecimalChecked::from_decimal(@decimal_gda.Decimal, @decimal_gda.GdaContext) -> Self
pub fn GdaDecimalChecked::from_outcome(@decimal_gda.GdaOutcome[@decimal_gda.Decimal]) -> Self
pub fn GdaDecimalChecked::is_trapped(Self) -> Bool
pub fn GdaDecimalChecked::ln(Self) -> Self
pub fn GdaDecimalChecked::log10(Self) -> Self
pub fn GdaDecimalChecked::minus(Self) -> Self
pub fn GdaDecimalChecked::multiply(Self, @decimal_gda.Decimal) -> Self
pub fn GdaDecimalChecked::next_minus(Self) -> Self
pub fn GdaDecimalChecked::next_plus(Self) -> Self
pub fn GdaDecimalChecked::next_toward(Self, @decimal_gda.Decimal) -> Self
pub fn GdaDecimalChecked::outcome(Self) -> @decimal_gda.GdaOutcome[@decimal_gda.Decimal]
pub fn GdaDecimalChecked::parse(String, @decimal_gda.GdaContext) -> Self
pub fn GdaDecimalChecked::plus(Self) -> Self
pub fn GdaDecimalChecked::power(Self, @decimal_gda.Decimal) -> Self
pub fn GdaDecimalChecked::quantize(Self, @decimal_gda.Decimal) -> Self
pub fn GdaDecimalChecked::raised(Self) -> @decimal_gda.GdaFlags
pub fn GdaDecimalChecked::reduce(Self) -> Self
pub fn GdaDecimalChecked::remainder(Self, @decimal_gda.Decimal) -> Self
pub fn GdaDecimalChecked::resume_defined(Self) -> Self
pub fn GdaDecimalChecked::sqrt(Self) -> Self
pub fn GdaDecimalChecked::status(Self) -> @decimal_gda.GdaFlags
pub fn GdaDecimalChecked::subtract(Self, @decimal_gda.Decimal) -> Self
pub fn GdaDecimalChecked::trapped_signal(Self) -> @decimal_gda.GdaSignal?
pub fn GdaDecimalChecked::value(Self) -> @decimal_gda.Decimal

// Type aliases

// Traits