decimal_checked API
decimal_checked provides DecimalChecked, an immutable pipeline state for
IEEE 754 decimal arithmetic with decimal. The state holds the
current value, one DecimalContext, the flags raised by the latest step, the
flags accumulated over all steps, and an optional ArithmeticError. Each
operation applies the matching Decimal::*_ctx operation under the stored
context. IEEE exceptional results (infinities, NaNs, rounded values) remain
values and only set flags; an error is recorded only when an elementary
function cannot certify its result, and it stops the pipeline. The
tutorial shows typical pipelines; the
design page models the state as a writer monad
over the flag monoid and proves that the accumulated flags are the union of the
per-step flags.
The examples list flags with this helper:
///|
fn flags(f : @decimal.DecimalFlags) -> 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),
("subnormal", f.subnormal),
("conversion_syntax", f.conversion_syntax),
("invalid_context", f.invalid_context),
]
[ for p in named if p.1 => p.0 ].join(",")
}
The state type
DecimalChecked
DecimalChecked is the state of an IEEE decimal pipeline.
pub struct DecimalChecked {
// private fields
}
Write a state as : value , context ,
raised flags of the latest step, accumulated flags , and error
(absent or one ArithmeticError). Every operation returns a new
state; nothing is mutated.
Construction
DecimalChecked::from_outcome
from_outcome(value, context, flags) starts a pipeline from a value and the
flags that produced it.
pub fn DecimalChecked::from_outcome(@decimal.Decimal, @decimal.DecimalContext, @decimal.DecimalFlags) -> Self
The result is with (on the current
branch ieee754() returns the context unchanged). The value is not
re-rounded; use it with the (value, flags) pair returned by a
Decimal::*_ctx call.
DecimalChecked::from_decimal
from_decimal(value, context) rounds a value into the context.
pub fn DecimalChecked::from_decimal(@decimal.Decimal, @decimal.DecimalContext) -> Self
It applies Decimal::apply_ctx, which rounds the value to the context’s
precision and exponent range, and records the resulting flags as both raised
and accumulated.
DecimalChecked::parse
parse(source, context) converts a decimal string under the context.
pub fn DecimalChecked::parse(String, @decimal.DecimalContext) -> Self
It uses Decimal::from_string_ctx. Extra digits are rounded (inexact,
rounded); an invalid string gives a NaN value with conversion_syntax raised,
not an error.
DecimalChecked::from_int, from_bigint, from_double, from_float
These constructors convert a MoonBit number.
pub fn DecimalChecked::from_int(Int, @decimal.DecimalContext) -> Self
pub fn DecimalChecked::from_bigint(@bigint.BigInt, @decimal.DecimalContext) -> Self
pub fn DecimalChecked::from_double(Double, @decimal.DecimalContext) -> Self
pub fn DecimalChecked::from_float(Float, @decimal.DecimalContext) -> Self
from_int and from_bigint parse the integer’s decimal string, so integers
longer than the precision are rounded with flags. from_double first converts
the binary value to a decimal of 17 significant digits and from_float to 9
digits (enough to identify the binary value), then rounds into the context with
from_decimal. So from_double(0.1, decimal64) is 0.1000000000000000 with
inexact and rounded raised.
///|
test "construction records conversion flags" {
let ctx = @decimal.DecimalContext::new(precision=5)
let parsed = @decimal_checked.DecimalChecked::parse("1.234567", ctx)
inspect(parsed.value().to_string(), content="1.2346")
inspect(flags(parsed.raised()), content="inexact,rounded")
let bad = @decimal_checked.DecimalChecked::parse("abc", ctx)
inspect(bad.value().to_string(), content="nan")
inspect(bad.is_ok(), content="true")
inspect(
flags(bad.raised()).contains("conversion_syntax"),
content="true",
)
let tenth = @decimal_checked.DecimalChecked::from_double(
0.1,
@decimal.DecimalContext::decimal64(),
)
inspect(tenth.value().to_string(), content="0.1000000000000000")
}
Observation
value, context, raised, flags
These accessors return the components of the state.
pub fn DecimalChecked::value(Self) -> @decimal.Decimal
pub fn DecimalChecked::context(Self) -> @decimal.DecimalContext
pub fn DecimalChecked::raised(Self) -> @decimal.DecimalFlags
pub fn DecimalChecked::flags(Self) -> @decimal.DecimalFlags
raised() is , the flags of the latest successful step only. flags() is
, the bitwise OR of the flags of every successful step since construction
or the last clear_flags. After an error, value, raised and flags keep
the state reached before the failing step.
outcome, result
These methods return the value with the accumulated flags.
pub fn DecimalChecked::outcome(Self) -> (@decimal.Decimal, @decimal.DecimalFlags)
pub fn DecimalChecked::result(Self) -> Result[(@decimal.Decimal, @decimal.DecimalFlags), @arithmetic.ArithmeticError]
outcome() is regardless of the error; result() is
when an error is recorded and
otherwise.
is_ok, is_err, error
These methods test for and return the recorded error.
pub fn DecimalChecked::is_ok(Self) -> Bool
pub fn DecimalChecked::is_err(Self) -> Bool
pub fn DecimalChecked::error(Self) -> @arithmetic.ArithmeticError?
Controlling the state
DecimalChecked::clear_flags
clear_flags() resets both and to the empty flag set.
pub fn DecimalChecked::clear_flags(Self) -> Self
Value, context and error are kept. It also applies to a state with an error.
DecimalChecked::with_context
with_context(context) switches to a new context and rounds the current value
into it.
pub fn DecimalChecked::with_context(Self, @decimal.DecimalContext) -> Self
The value is re-applied with apply_ctx under the new context; the new flags
become and are ORed into . On a state with an error it returns the state
unchanged.
DecimalChecked::apply
apply() rounds the current value into the stored context and records the
flags.
pub fn DecimalChecked::apply(Self) -> Self
///|
test "raised versus accumulated flags" {
let ctx = @decimal.DecimalContext::new(precision=5)
let start = @decimal_checked.DecimalChecked::parse("1.234567", ctx)
let step = start.add(@decimal.Decimal::from_int(1))
inspect(flags(step.raised()), content="")
inspect(flags(step.flags()), content="inexact,rounded")
let infinite = step.div(@decimal.Decimal::zero())
inspect(infinite.value().to_string(), content="inf")
inspect(flags(infinite.raised()), content="division_by_zero")
inspect(flags(infinite.flags()), content="inexact,rounded,division_by_zero")
inspect(flags(infinite.clear_flags().flags()), content="")
let wider = @decimal_checked.DecimalChecked::parse(
"1.234567",
@decimal.DecimalContext::decimal64(),
).with_context(ctx)
inspect(wider.value().to_string(), content="1.2346")
}
Operations that cannot fail
plus, minus, abs, add, sub, mul, div, fma, sqrt, quantize, remainder, reduce, min, max, next_minus, next_plus, next_toward
These methods apply the IEEE decimal operation of the same meaning to the
current value and a plain Decimal operand under the stored context.
pub fn DecimalChecked::plus(Self) -> Self
pub fn DecimalChecked::minus(Self) -> Self
pub fn DecimalChecked::abs(Self) -> Self
pub fn DecimalChecked::add(Self, @decimal.Decimal) -> Self
pub fn DecimalChecked::sub(Self, @decimal.Decimal) -> Self
pub fn DecimalChecked::mul(Self, @decimal.Decimal) -> Self
pub fn DecimalChecked::div(Self, @decimal.Decimal) -> Self
pub fn DecimalChecked::fma(Self, @decimal.Decimal, @decimal.Decimal) -> Self
pub fn DecimalChecked::sqrt(Self) -> Self
pub fn DecimalChecked::quantize(Self, @decimal.Decimal) -> Self
pub fn DecimalChecked::remainder(Self, @decimal.Decimal) -> Self
pub fn DecimalChecked::reduce(Self) -> Self
pub fn DecimalChecked::min(Self, @decimal.Decimal) -> Self
pub fn DecimalChecked::max(Self, @decimal.Decimal) -> Self
pub fn DecimalChecked::next_minus(Self) -> Self
pub fn DecimalChecked::next_plus(Self) -> Self
pub fn DecimalChecked::next_toward(Self, @decimal.Decimal) -> Self
| Method | Delegates to | Result |
|---|---|---|
plus, minus, abs | plus_ctx, minus_ctx, abs_ctx | , , rounded into the context |
add, sub, mul, div | add_ctx, … | correctly rounded |
fma(m, a) | fma_ctx | with one rounding |
sqrt | sqrt_ctx | correctly rounded |
quantize(q) | quantize | rounded to the exponent of q |
remainder(d) | remainder_ctx | |
reduce | reduce_ctx | with trailing zeros removed |
min, max | min_ctx, max_ctx | IEEE minimum / maximum |
next_minus, next_plus, next_toward(t) | same names | adjacent representable value |
If an error is recorded, each method returns the state unchanged. Otherwise
the step gives the new state . Invalid operations produce NaN with
invalid_operation, division by zero produces an infinity with
division_by_zero, overflow produces an infinity or the largest finite number
with overflow, inexact and rounded, as in IEEE 754 decimal arithmetic.
///|
test "exceptional results are values" {
let ctx = @decimal.DecimalContext::decimal64()
let negative = @decimal_checked.DecimalChecked::from_int(-1, ctx).sqrt()
inspect(negative.value().to_string(), content="nan")
inspect(flags(negative.raised()), content="invalid_operation")
inspect(negative.is_ok(), content="true")
let big = @decimal_checked.DecimalChecked::parse("9e384", ctx).mul(
@decimal.Decimal::from_int(10),
)
inspect(big.value().to_string(), content="inf")
inspect(flags(big.raised()), content="inexact,rounded,overflow")
let cents = @decimal_checked.DecimalChecked::parse("2.675", ctx).quantize(
@decimal.Decimal::from_string("0.01").unwrap(),
)
inspect(cents.value().to_string(), content="2.68")
}
Operations that can record an error
Elementary functions
exp, exp2, exp10, expm1, ln, log2, log10, log1p, power,
pown, rootn, hypot, sin, cos, tan, sinpi, cospi, tanpi,
asin, acos, atan, atan2, sinh, cosh, tanh, asinh, acosh and
atanh apply the certified Decimal::try_*_ctx functions.
pub fn DecimalChecked::exp(Self) -> Self
pub fn DecimalChecked::exp2(Self) -> Self
pub fn DecimalChecked::exp10(Self) -> Self
pub fn DecimalChecked::expm1(Self) -> Self
pub fn DecimalChecked::ln(Self) -> Self
pub fn DecimalChecked::log2(Self) -> Self
pub fn DecimalChecked::log10(Self) -> Self
pub fn DecimalChecked::log1p(Self) -> Self
pub fn DecimalChecked::power(Self, @decimal.Decimal) -> Self
pub fn DecimalChecked::pown(Self, Int) -> Self
pub fn DecimalChecked::rootn(Self, Int) -> Self
pub fn DecimalChecked::hypot(Self, @decimal.Decimal) -> Self
pub fn DecimalChecked::sin(Self) -> Self
pub fn DecimalChecked::cos(Self) -> Self
pub fn DecimalChecked::tan(Self) -> Self
pub fn DecimalChecked::sinpi(Self) -> Self
pub fn DecimalChecked::cospi(Self) -> Self
pub fn DecimalChecked::tanpi(Self) -> Self
pub fn DecimalChecked::asin(Self) -> Self
pub fn DecimalChecked::acos(Self) -> Self
pub fn DecimalChecked::atan(Self) -> Self
pub fn DecimalChecked::atan2(Self, @decimal.Decimal) -> Self
pub fn DecimalChecked::sinh(Self) -> Self
pub fn DecimalChecked::cosh(Self) -> Self
pub fn DecimalChecked::tanh(Self) -> Self
pub fn DecimalChecked::asinh(Self) -> Self
pub fn DecimalChecked::acosh(Self) -> Self
pub fn DecimalChecked::atanh(Self) -> Self
The results are correctly rounded into the context. Domain violations are IEEE
values with flags (ln of a negative number is NaN with invalid_operation,
tanpi(0.5) is an infinity with division_by_zero). An error is recorded only
when try_*_ctx returns Err, which these functions do for a
CertificationFailure (the correctly rounded result could not be certified
within the refinement budget). On error the new state is
: the value and flags before the step are kept.
///|
test "elementary functions under a bounded context" {
let ctx = @decimal.DecimalContext::decimal64()
let ln2 = @decimal_checked.DecimalChecked::from_int(2, ctx).ln()
inspect(ln2.value().to_string(), content="0.6931471805599453")
inspect(flags(ln2.raised()), content="inexact,rounded")
let unbounded = @decimal_checked.DecimalChecked::from_int(
2,
@decimal.DecimalContext::new(precision=16),
).ln()
inspect(unbounded.value().to_string(), content="nan")
inspect(flags(unbounded.raised()), content="invalid_context")
let pole = @decimal_checked.DecimalChecked::parse("0.5", ctx).tanpi()
inspect(flags(pole.raised()), content="division_by_zero")
}
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_checked"
import {
"Luna-Flow/arithmetic",
"Luna-Flow/floating/decimal",
"moonbitlang/core/bigint",
}
// Values
// Errors
// Types and methods
pub struct DecimalChecked {
// private fields
}
pub fn DecimalChecked::abs(Self) -> Self
pub fn DecimalChecked::acos(Self) -> Self
pub fn DecimalChecked::acosh(Self) -> Self
pub fn DecimalChecked::add(Self, @decimal.Decimal) -> Self
pub fn DecimalChecked::apply(Self) -> Self
pub fn DecimalChecked::asin(Self) -> Self
pub fn DecimalChecked::asinh(Self) -> Self
pub fn DecimalChecked::atan(Self) -> Self
pub fn DecimalChecked::atan2(Self, @decimal.Decimal) -> Self
pub fn DecimalChecked::atanh(Self) -> Self
pub fn DecimalChecked::clear_flags(Self) -> Self
pub fn DecimalChecked::context(Self) -> @decimal.DecimalContext
pub fn DecimalChecked::cos(Self) -> Self
pub fn DecimalChecked::cosh(Self) -> Self
pub fn DecimalChecked::cospi(Self) -> Self
pub fn DecimalChecked::div(Self, @decimal.Decimal) -> Self
pub fn DecimalChecked::error(Self) -> @arithmetic.ArithmeticError?
pub fn DecimalChecked::exp(Self) -> Self
pub fn DecimalChecked::exp10(Self) -> Self
pub fn DecimalChecked::exp2(Self) -> Self
pub fn DecimalChecked::expm1(Self) -> Self
pub fn DecimalChecked::flags(Self) -> @decimal.DecimalFlags
pub fn DecimalChecked::fma(Self, @decimal.Decimal, @decimal.Decimal) -> Self
pub fn DecimalChecked::from_bigint(@bigint.BigInt, @decimal.DecimalContext) -> Self
pub fn DecimalChecked::from_decimal(@decimal.Decimal, @decimal.DecimalContext) -> Self
pub fn DecimalChecked::from_double(Double, @decimal.DecimalContext) -> Self
pub fn DecimalChecked::from_float(Float, @decimal.DecimalContext) -> Self
pub fn DecimalChecked::from_int(Int, @decimal.DecimalContext) -> Self
pub fn DecimalChecked::from_outcome(@decimal.Decimal, @decimal.DecimalContext, @decimal.DecimalFlags) -> Self
pub fn DecimalChecked::hypot(Self, @decimal.Decimal) -> Self
pub fn DecimalChecked::is_err(Self) -> Bool
pub fn DecimalChecked::is_ok(Self) -> Bool
pub fn DecimalChecked::ln(Self) -> Self
pub fn DecimalChecked::log10(Self) -> Self
pub fn DecimalChecked::log1p(Self) -> Self
pub fn DecimalChecked::log2(Self) -> Self
pub fn DecimalChecked::max(Self, @decimal.Decimal) -> Self
pub fn DecimalChecked::min(Self, @decimal.Decimal) -> Self
pub fn DecimalChecked::minus(Self) -> Self
pub fn DecimalChecked::mul(Self, @decimal.Decimal) -> Self
pub fn DecimalChecked::next_minus(Self) -> Self
pub fn DecimalChecked::next_plus(Self) -> Self
pub fn DecimalChecked::next_toward(Self, @decimal.Decimal) -> Self
pub fn DecimalChecked::outcome(Self) -> (@decimal.Decimal, @decimal.DecimalFlags)
pub fn DecimalChecked::parse(String, @decimal.DecimalContext) -> Self
pub fn DecimalChecked::plus(Self) -> Self
pub fn DecimalChecked::power(Self, @decimal.Decimal) -> Self
pub fn DecimalChecked::pown(Self, Int) -> Self
pub fn DecimalChecked::quantize(Self, @decimal.Decimal) -> Self
pub fn DecimalChecked::raised(Self) -> @decimal.DecimalFlags
pub fn DecimalChecked::reduce(Self) -> Self
pub fn DecimalChecked::remainder(Self, @decimal.Decimal) -> Self
pub fn DecimalChecked::result(Self) -> Result[(@decimal.Decimal, @decimal.DecimalFlags), @arithmetic.ArithmeticError]
pub fn DecimalChecked::rootn(Self, Int) -> Self
pub fn DecimalChecked::sin(Self) -> Self
pub fn DecimalChecked::sinh(Self) -> Self
pub fn DecimalChecked::sinpi(Self) -> Self
pub fn DecimalChecked::sqrt(Self) -> Self
pub fn DecimalChecked::sub(Self, @decimal.Decimal) -> Self
pub fn DecimalChecked::tan(Self) -> Self
pub fn DecimalChecked::tanh(Self) -> Self
pub fn DecimalChecked::tanpi(Self) -> Self
pub fn DecimalChecked::value(Self) -> @decimal.Decimal
pub fn DecimalChecked::with_context(Self, @decimal.DecimalContext) -> Self
// Type aliases
// Traits