decimal_checked API

decimal_checked 提供 DecimalChecked,这是基于 decimal 的 IEEE 754 十进制算术的不可变流水线状态。该状态保存当前值、一个 DecimalContext、最近一步引发的标志、所有步骤累积的标志,以及一个可选的 ArithmeticError。每个运算都在所存储的上下文下执行对应的 Decimal::*_ctx 运算。IEEE 异常结果(无穷、NaN、舍入后的值)仍然是值,只设置标志;只有当初等函数无法认证其结果时才记录错误,并使流水线停止。教程展示了典型的流水线;设计页面将该状态建模为标志幺半群上的 writer 单子,并证明累积标志是各步标志的并。

示例使用如下辅助函数列出标志:

///|
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(",")
}

状态类型

DecimalChecked

DecimalChecked 是 IEEE 十进制流水线的状态。

pub struct DecimalChecked {
  // private fields
}

将状态记为 σ=(v,c,r,F,ε)\sigma = (v, c, r, F, \varepsilon):值 vv、上下文 cc、最近一步引发的标志 rr、累积标志 FF,以及错误 ε\varepsilon(不存在或为一个 ArithmeticError)。每个运算都返回新的状态;不修改任何东西。

构造

DecimalChecked::from_outcome

from_outcome(value, context, flags) 由一个值及产生它的标志开始一条流水线。

pub fn DecimalChecked::from_outcome(@decimal.Decimal, @decimal.DecimalContext, @decimal.DecimalFlags) -> Self

结果为 (value,c′,flags,flags,none)(\textit{value}, c', \textit{flags}, \textit{flags}, \text{none}),其中 c′=context.ieee754()c' = \textit{context}.\texttt{ieee754()}(在当前分支上 ieee754() 原样返回上下文)。值不会被重新舍入;请将它与 Decimal::*_ctx 调用返回的 (value, flags) 二元组配合使用。

DecimalChecked::from_decimal

from_decimal(value, context) 将值舍入到上下文中。

pub fn DecimalChecked::from_decimal(@decimal.Decimal, @decimal.DecimalContext) -> Self

它执行 Decimal::apply_ctx,将值舍入到上下文的精度和指数范围,并把所得标志同时记录为引发标志和累积标志。

DecimalChecked::parse

parse(source, context) 在上下文下转换十进制字符串。

pub fn DecimalChecked::parse(String, @decimal.DecimalContext) -> Self

它使用 Decimal::from_string_ctx。多余的数字会被舍入(inexact、rounded);无效字符串得到一个 NaN 值并引发 conversion_syntax,而不是错误。

DecimalChecked::from_int, from_bigint, from_double, from_float

这些构造函数转换 MoonBit 数值。

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 和 from_bigint 解析整数的十进制字符串,因此长于精度的整数会被舍入并带有标志。from_double 先将二进制值转换为 17 位有效数字的十进制数,from_float 则转换为 9 位(足以唯一确定该二进制值),然后用 from_decimal 舍入到上下文中。因此 from_double(0.1, decimal64) 为 0.1000000000000000,并引发 inexact 和 rounded。

///|
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")
}

观察

value, context, raised, flags

这些访问函数返回状态的各个分量。

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() 即 rr,仅为最近一次成功步骤的标志。flags() 即 FF,是自构造或上次 clear_flags 以来所有成功步骤标志的按位 OR。出错之后,value、raised 和 flags 保持失败步骤之前所达到的状态。

outcome, result

这些方法返回值及累积标志。

pub fn DecimalChecked::outcome(Self) -> (@decimal.Decimal, @decimal.DecimalFlags)
pub fn DecimalChecked::result(Self) -> Result[(@decimal.Decimal, @decimal.DecimalFlags), @arithmetic.ArithmeticError]

outcome() 无论是否有错误都为 (v,F)(v, F);result() 在记录了错误时为 Err(ε)\mathrm{Err}(\varepsilon),否则为 Ok((v,F))\mathrm{Ok}((v, F))。

is_ok, is_err, error

这些方法检测并返回所记录的错误。

pub fn DecimalChecked::is_ok(Self) -> Bool
pub fn DecimalChecked::is_err(Self) -> Bool
pub fn DecimalChecked::error(Self) -> @arithmetic.ArithmeticError?

控制状态

DecimalChecked::clear_flags

clear_flags() 将 rr 和 FF 都重置为空标志集。

pub fn DecimalChecked::clear_flags(Self) -> Self

值、上下文与错误保持不变。它同样适用于带有错误的状态。

DecimalChecked::with_context

with_context(context) 切换到新的上下文,并将当前值舍入到其中。

pub fn DecimalChecked::with_context(Self, @decimal.DecimalContext) -> Self

值在新上下文下用 apply_ctx 重新应用;新的标志成为 rr,并 OR 到 FF 中。对带有错误的状态,它原样返回该状态。

DecimalChecked::apply

apply() 将当前值舍入到所存储的上下文中,并记录标志。

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")
}

不会失败的运算

plus, minus, abs, add, sub, mul, div, fma, sqrt, quantize, remainder, reduce, min, max, next_minus, next_plus, next_toward

这些方法在所存储的上下文下,对当前值和一个普通 Decimal 操作数执行含义相同的 IEEE 十进制运算。

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
方法委托给结果
plus, minus, absplus_ctx, minus_ctx, abs_ctx舍入到上下文的 +v+v、−v-v、∣v∣\lvert v\rvert
add, sub, mul, divadd_ctx, …正确舍入的 v∘yv \circ y
fma(m, a)fma_ctx只舍入一次的 v⋅m+av \cdot m + a
sqrtsqrt_ctx正确舍入的 v\sqrt{v}
quantize(q)quantize舍入到 q 的指数的 vv
remainder(d)remainder_ctxv−d⋅trunc⁡(v/d)v - d \cdot \operatorname{trunc}(v/d)
reducereduce_ctx去除尾随零后的 vv
min, maxmin_ctx, max_ctxIEEE 最小值 / 最大值
next_minus, next_plus, next_toward(t)同名相邻的可表示值

若已记录错误,每个方法都原样返回状态。否则,步骤 (v′,r′)=op(v,c)(v', r') = \mathrm{op}(v, c) 给出新状态 (v′,c,r′,F∨r′,none)(v', c, r', F \lor r', \text{none})。与 IEEE 754 十进制算术一致:无效运算产生带 invalid_operation 的 NaN,除以零产生带 division_by_zero 的无穷,上溢产生无穷或最大有限数,并带 overflow、inexact 和 rounded。

///|
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")
}

可能记录错误的运算

初等函数

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 和 atanh 应用经过认证的 Decimal::try_*_ctx 函数。

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

结果被正确舍入到上下文中。定义域违例是带标志的 IEEE 值(负数的 ln 是带 invalid_operation 的 NaN,tanpi(0.5) 是带 division_by_zero 的无穷)。只有当 try_*_ctx 返回 Err 时才记录错误,这些函数在发生 CertificationFailure(无法在细化预算内认证正确舍入的结果)时会如此。出错时新状态为 (v,c,r,F,Some(e))(v, c, r, F, \mathrm{Some}(e)):保留该步骤之前的值和标志。

///|
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")
}

完整公共接口

以下快照是该包完整的生成接口。

// 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