decimal_checked API

decimal_checked は、decimal による IEEE 754 十進演算のための不変なパイプライン状態 DecimalChecked を提供します。この状態は、現在の値、1 つの 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(なし、または 1 つの 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)、無効な文字列はエラーではなく、conversion_syntax が立った NaN 値になります。

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 となり、FF に OR されます。エラーを持つ状態では、状態をそのまま返します。

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_ctx1 回の丸めによる v⋅m+av \cdot m + a
sqrtsqrt_ctx正しく丸められた v\sqrt{v}
quantize(q)quantizeq の指数に合わせて丸めた 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