core API
このページは Luna-Flow/arithmetic パッケージ(src/ にあり、core として文書化される)のすべての公開項目を列挙する。例ではこれを @lf_arith としてインポートする。
import {
"Luna-Flow/arithmetic" @lf_arith,
}
項目は四つの能力層と、それらが共有する値に分かれる。
| 層 | 典型的なシグネチャ | 失敗の伝え方 | 定義場所 |
|---|---|---|---|
| 非検査 | fn sqrt(Self) -> Self | なし:バックエンドが決める(NaN、中断、ラップアラウンド) | elementary.mbt |
| 検査付き | fn sqrt_checked(Self, ArithmeticContext) -> Result[Self, ArithmeticError] | Err(ArithmeticError) | checked.mbt |
| コンテキスト付き | fn sqrt_contextual(Self, ArithmeticContext) -> Result[ArithmeticOutcome[Self], ArithmeticError] | 拒否は Err、注目すべき成功は診断 | contextual.mbt |
| 包含区間の関係 | fn definitely_lt(Self, Self) -> Bool | なし:順序ではなく関係 | checked.mbt |
core 設計ページ は層を分ける理由と、その背後にある数学を導出する。core チュートリアル は具体的な作業の中でそれらを使う。
共有語彙
FpClass
FpClass は浮動小数点値を互いに素な三つのクラスのいずれかに分類する。
pub(all) enum FpClass {
Finite
Infinity
NaN
} derive(Eq, @debug.Debug)
Finite はゼロ、非正規化数、正規化数を含み、Infinity は符号付きの両無限大を、NaN はすべての非数エンコーディングを含む。これらのクラスは分割をなすので、すべての値にちょうど一つが当てはまる。値のクラスは NumericFormatContextual::classify_contextual で得る。
test "classify a few doubles" {
let inf = 1.0 / 0.0
debug_inspect(
@lf_arith.NumericFormatContextual::classify_contextual(inf),
content="Infinity",
)
debug_inspect(
@lf_arith.NumericFormatContextual::classify_contextual(0.0 / 0.0),
content="NaN",
)
debug_inspect(
@lf_arith.NumericFormatContextual::classify_contextual(-0.0),
content="Finite",
)
}
RoundingMode
RoundingMode はコンテキストが要求する丸めの方向を表す。
pub(all) enum RoundingMode {
ToNearestEven
TowardZero
TowardPositive
TowardNegative
AwayFromZero
} derive(Eq)
隣り合う二つの表現可能な値 の間にある実数 について:
| モード | 戻り値 |
|---|---|
ToNearestEven | のうち近い方。等距離なら最後の桁が偶数の方 |
TowardZero | 絶対値の小さい方(切り捨て) |
TowardPositive | (天井) |
TowardNegative | (床) |
AwayFromZero | 絶対値の大きい方 |
AwayFromZero はゼロから遠ざかる方向への有向丸め(General Decimal Arithmetic 仕様の ROUND_UP)であり、等距離時にゼロから遠ざける最近接丸めではない。モードは要求にすぎない。パッケージはそれを ArithmeticContext に保存し、従うかどうかは各バックエンドが決める。組み込みの Float と Double のインスタンスはこれを無視する。
算術コンテキスト
ArithmeticContext
ArithmeticContext は、検査付き演算やコンテキスト付き演算が実行されるときの作業精度、丸めモード、指数範囲を記録する不変のレコードである。
pub struct ArithmeticContext {
precision : Int
rounding : RoundingMode
e_min : Int?
e_max : Int?
clamp : Bool
} derive(Eq)
precisionはバックエンドの基数での仮数の桁数である(十進バックエンドなら十進桁、二進バックエンドならビット)。常に1以上である。roundingは要求されるRoundingModeである。e_minとe_maxは正規化された結果の調整済み指数を制限する。Noneは無制限を意味する。両方があるときはe_min <= e_maxである。clampは、十進交換形式と同様に、指数を形式の範囲へクランプするようバックエンドに求める。
フィールドはパッケージ外からは読み取り専用である。値は ArithmeticContext::new かプリセットで作る。コンテキストは通常の引数として渡され、グローバルなコンテキストもスレッドローカルなコンテキストも存在しない。
ArithmeticContext::new
ArithmeticContext::new は精度と省略可能な設定からコンテキストを作る。
pub fn ArithmeticContext::new(Int, rounding? : RoundingMode, e_min? : Int, e_max? : Int, clamp? : Bool) -> ArithmeticContext
rounding の既定値は ToNearestEven、clamp は false、二つの指数制限はどちらも None である。1 未満の精度は 1 に引き上げられる。両方の制限が与えられ e_min > e_max のとき、この関数は ArithmeticContext::new: e_min must not exceed e_max で中断する。
test "build a context" {
let ctx = @lf_arith.ArithmeticContext::new(
24,
rounding=@lf_arith.RoundingMode::TowardNegative,
e_min=-126,
e_max=127,
)
inspect(ctx.precision, content="24")
inspect(ctx.clamp, content="false")
inspect(@lf_arith.ArithmeticContext::new(0).precision, content="1")
}
ArithmeticContext::decimal32, ArithmeticContext::decimal64, ArithmeticContext::decimal128
これらのプリセットは IEEE 754 十進交換形式のコンテキストを返す。
pub fn ArithmeticContext::decimal32() -> ArithmeticContext
pub fn ArithmeticContext::decimal64() -> ArithmeticContext
pub fn ArithmeticContext::decimal128() -> ArithmeticContext
| プリセット | precision | e_min | e_max | rounding | clamp |
|---|---|---|---|---|---|
decimal32 | 7 | -95 | 96 | ToNearestEven | true |
decimal64 | 16 | -383 | 384 | ToNearestEven | true |
decimal128 | 34 | -6143 | 6144 | ToNearestEven | true |
IEEE 754 が要求するとおり、どのプリセットでも である。
test "decimal presets" {
let d64 = @lf_arith.ArithmeticContext::decimal64()
inspect(d64.precision, content="16")
inspect(d64.e_min == Some(-383), content="true")
inspect(d64 == @lf_arith.ArithmeticContext::new(16, e_min=-383, e_max=384, clamp=true), content="true")
}
エラー
ArithmeticErrorKind
ArithmeticErrorKind は ArithmeticError の種別である。
pub enum ArithmeticErrorKind {
DivisionByZero
ParseError
DomainError
FormatError
UnsupportedOperation
UnorderedComparison
CertificationFailure(CertificationFailureDetail)
} derive(Eq)
| 種別 | 意味 |
|---|---|
DivisionByZero | ゼロ以外の値または NaN をゼロで割った、あるいはゼロの逆数を取った |
ParseError | 値を表さないテキスト |
DomainError | 演算の数学的な定義域外の引数。 のような不定形を含む |
FormatError | 対象の形式が保持または表示できない値 |
UnsupportedOperation | バックエンドが実装していない演算またはコンテキスト |
UnorderedComparison | NaN のような順序付けられない値を含む比較 |
CertificationFailure | 証明付きバックエンドが結果を証明できなかった。詳細を保持する |
この列挙型は pub(all) ではなく pub である。マッチはできるが、エラーは下記の ArithmeticError コンストラクタで作る。
ArithmeticError
ArithmeticError は、すべての検査付きトレイトとコンテキスト付きトレイトが返す構造化エラーである。
pub struct ArithmeticError {
kind : ArithmeticErrorKind
message : String
} derive(Eq)
kind は機械可読な種別、message は人間向けの説明である。二つのエラーは両方のフィールドが等しいときに等しい。
エラーのコンストラクタ
ArithmeticError::division_by_zero、ArithmeticError::parse_error、ArithmeticError::domain_error、ArithmeticError::format_error、ArithmeticError::unsupported、ArithmeticError::unordered_comparison は、与えられたメッセージを持つ対応する種別のエラーを作る。
pub fn ArithmeticError::division_by_zero(String) -> ArithmeticError
pub fn ArithmeticError::parse_error(String) -> ArithmeticError
pub fn ArithmeticError::domain_error(String) -> ArithmeticError
pub fn ArithmeticError::format_error(String) -> ArithmeticError
pub fn ArithmeticError::unsupported(String) -> ArithmeticError
pub fn ArithmeticError::unordered_comparison(String) -> ArithmeticError
unsupported が作る種別は UnsupportedOperation である。
エラーの述語
ArithmeticError::is_division_by_zero、is_parse_error、is_domain_error、is_format_error、is_unsupported、is_unordered_comparison、is_certification_failure はエラーの種別を判定する。
pub fn ArithmeticError::is_division_by_zero(ArithmeticError) -> Bool
pub fn ArithmeticError::is_parse_error(ArithmeticError) -> Bool
pub fn ArithmeticError::is_domain_error(ArithmeticError) -> Bool
pub fn ArithmeticError::is_format_error(ArithmeticError) -> Bool
pub fn ArithmeticError::is_unsupported(ArithmeticError) -> Bool
pub fn ArithmeticError::is_unordered_comparison(ArithmeticError) -> Bool
pub fn ArithmeticError::is_certification_failure(ArithmeticError) -> Bool
どのエラーについても、ちょうど一つの述語が真になる。
test "build and classify errors" {
let err = @lf_arith.ArithmeticError::domain_error("log of a negative number")
inspect(err.is_domain_error(), content="true")
inspect(err.is_division_by_zero(), content="false")
inspect(err.message, content="log of a negative number")
inspect(
@lf_arith.ArithmeticError::unsupported("no decimal backend").is_unsupported(),
content="true",
)
}
ArithmeticError::certification_failure
ArithmeticError::certification_failure は CertificationFailureDetail を種別 CertificationFailure のエラーに包む。
pub fn ArithmeticError::certification_failure(CertificationFailureDetail) -> ArithmeticError
メッセージは certified evaluation failed for <operation> であり、<operation> は詳細から取られる。
ArithmeticError::certification_failure_detail
ArithmeticError::certification_failure_detail は証明失敗の詳細を返し、それ以外の種別では None を返す。
pub fn ArithmeticError::certification_failure_detail(ArithmeticError) -> CertificationFailureDetail?
証明の失敗
証明付きバックエンドは段階からなるパイプラインで関数を評価し、結果が目標を満たすことを証明する。それができないときは、パイプラインがどこで止まり、なぜ止まったかを報告する。設計ページ がこのパイプラインを説明する。
CertificationStage
CertificationStage は証明付き評価が失敗した段階を表す。
pub(all) enum CertificationStage {
RangeReduction
SeriesEvaluation
EnclosurePropagation
TargetRounding
} derive(Eq)
| 段階 | その段階で行う処理 |
|---|---|
RangeReduction | 引数を小さな主区間へ還元する |
SeriesEvaluation | 打ち切り級数や近似を剰余の上界とともに評価する |
EnclosurePropagation | 残りの演算を通して誤差の包含区間を伝播する |
TargetRounding | 最終的な包含区間を目標精度へ丸める |
CertificationFailureReason
CertificationFailureReason は段階を証明できなかった理由を示す。
pub(all) enum CertificationFailureReason {
RangeNotCertified
SeriesDidNotConverge
InvalidEnclosure
ResourceLimit
RefinementBudgetExhausted
} derive(Eq)
| 理由 | 意味 |
|---|---|
RangeNotCertified | 還元後の引数が主範囲にあることを示せなかった |
SeriesDidNotConverge | 剰余の上界が要求された許容誤差を下回らなかった |
InvalidEnclosure | 途中の包含区間が空、非有界、またはその他の理由で使えなかった |
ResourceLimit | 精度またはサイズの上限に達した |
RefinementBudgetExhausted | 精度を繰り返し上げても丸めが決まらなかった |
CertificationFailureDetail
CertificationFailureDetail は証明失敗の証拠を記録する。
pub struct CertificationFailureDetail {
operation : String
stage : CertificationStage
reason : CertificationFailureReason
target_precision : Int
work_precision : Int
refinements : Int
} derive(Eq)
operation は演算の名前(例えば "exp")、target_precision は結果が要求された精度、work_precision は最後に試した内部精度、refinements は精度を上げた回数である。
CertificationFailureDetail::new
CertificationFailureDetail::new は詳細を作り、カウンタを正規化する。
pub fn CertificationFailureDetail::new(String, CertificationStage, CertificationFailureReason, Int, Int, Int) -> CertificationFailureDetail
引数は順に、演算、段階、理由、目標精度、作業精度、精度向上の回数である。二つの精度は 1 以上に、回数は 0 以上に引き上げられる。
詳細のアクセサ
CertificationFailureDetail::operation、stage、reason、target_precision、work_precision、refinements は同名のフィールドを返す。
pub fn CertificationFailureDetail::operation(CertificationFailureDetail) -> String
pub fn CertificationFailureDetail::stage(CertificationFailureDetail) -> CertificationStage
pub fn CertificationFailureDetail::reason(CertificationFailureDetail) -> CertificationFailureReason
pub fn CertificationFailureDetail::target_precision(CertificationFailureDetail) -> Int
pub fn CertificationFailureDetail::work_precision(CertificationFailureDetail) -> Int
pub fn CertificationFailureDetail::refinements(CertificationFailureDetail) -> Int
test "certification failure round trip" {
let detail = @lf_arith.CertificationFailureDetail::new(
"exp",
@lf_arith.CertificationStage::TargetRounding,
@lf_arith.CertificationFailureReason::RefinementBudgetExhausted,
53,
384,
-2,
)
let err = @lf_arith.ArithmeticError::certification_failure(detail)
inspect(err.message, content="certified evaluation failed for exp")
inspect(err.is_certification_failure(), content="true")
guard err.certification_failure_detail() is Some(d) else { fail("no detail") }
inspect(d.work_precision(), content="384")
inspect(d.refinements(), content="0")
inspect(d.stage() == @lf_arith.CertificationStage::TargetRounding, content="true")
}
診断と結果
ArithmeticDiagnostics
ArithmeticDiagnostics は、成功したコンテキスト付き演算が立てうる六つの条件フラグの集合である。
pub struct ArithmeticDiagnostics {
inexact : Bool
rounded : Bool
overflow : Bool
underflow : Bool
subnormal : Bool
clamped : Bool
} derive(Eq, @debug.Debug)
| フラグ | 立つ条件 |
|---|---|
inexact | 返された値が正確な結果と異なる |
rounded | 結果がコンテキストの精度へ丸められた(損失がない場合もある) |
overflow | 正確な結果が最大の有限値の大きさを超えた |
underflow | 結果が極めて小さく(正規範囲未満)、かつ不正確である |
subnormal | 結果が正規範囲未満である |
clamped | 形式に収めるため指数が変更された |
意味は IEEE 754 と General Decimal Arithmetic の条件に従う。バックエンドは検出できるフラグだけを立てる。
ArithmeticDiagnostics::empty
ArithmeticDiagnostics::empty はすべてのフラグが false の値を返す。
pub fn ArithmeticDiagnostics::empty() -> ArithmeticDiagnostics
ArithmeticDiagnostics::new
ArithmeticDiagnostics::new はラベル付きのフラグから診断を作る。各フラグの既定値は false である。
pub fn ArithmeticDiagnostics::new(inexact? : Bool, rounded? : Bool, overflow? : Bool, underflow? : Bool, subnormal? : Bool, clamped? : Bool) -> ArithmeticDiagnostics
ArithmeticDiagnostics::combine
ArithmeticDiagnostics::combine は二つの診断を、各フラグの論理和で併合する。
pub fn ArithmeticDiagnostics::combine(ArithmeticDiagnostics, ArithmeticDiagnostics) -> ArithmeticDiagnostics
combine は結合的、可換、冪等であり、empty() がその単位元である。したがって計算の診断をどの順序で畳み込んでも同じ結果になる。
test "combine diagnostics" {
let a = @lf_arith.ArithmeticDiagnostics::new(inexact=true, rounded=true)
let b = @lf_arith.ArithmeticDiagnostics::new(underflow=true)
let both = a.combine(b)
inspect(both.inexact && both.underflow, content="true")
inspect(both.overflow, content="false")
inspect(a.combine(@lf_arith.ArithmeticDiagnostics::empty()) == a, content="true")
inspect(a.combine(b) == b.combine(a), content="true")
}
ArithmeticOutcome
ArithmeticOutcome[T] は、成功したコンテキスト付き演算の値とその診断を組にする。
pub struct ArithmeticOutcome[T] {
value : T
diagnostics : ArithmeticDiagnostics
} derive(Eq, @debug.Debug)
ArithmeticOutcome::exact
ArithmeticOutcome::exact は値を空の診断で包む。
pub fn[T] ArithmeticOutcome::exact(T) -> ArithmeticOutcome[T]
ArithmeticOutcome::with_diagnostics
ArithmeticOutcome::with_diagnostics は値を与えられた診断で包む。
pub fn[T] ArithmeticOutcome::with_diagnostics(T, ArithmeticDiagnostics) -> ArithmeticOutcome[T]
test "build outcomes" {
let exact = @lf_arith.ArithmeticOutcome::exact(2.5)
inspect(exact.value, content="2.5")
inspect(exact.diagnostics == @lf_arith.ArithmeticDiagnostics::empty(), content="true")
let rounded = @lf_arith.ArithmeticOutcome::with_diagnostics(
0.1,
@lf_arith.ArithmeticDiagnostics::new(inexact=true, rounded=true),
)
inspect(rounded.diagnostics.inexact, content="true")
}
等価性
FpClass::equal、RoundingMode::equal、ArithmeticContext::equal、ArithmeticDiagnostics::equal、ArithmeticOutcome::equal、ArithmeticError::equal、ArithmeticErrorKind::equal、CertificationStage::equal、CertificationFailureReason::equal、CertificationFailureDetail::equal は導出された Eq インスタンスをメソッドとして昇格したものである。
pub fn FpClass::equal(FpClass, FpClass) -> Bool
pub fn RoundingMode::equal(RoundingMode, RoundingMode) -> Bool
pub fn ArithmeticContext::equal(ArithmeticContext, ArithmeticContext) -> Bool
pub fn ArithmeticDiagnostics::equal(ArithmeticDiagnostics, ArithmeticDiagnostics) -> Bool
pub fn[T : Eq] ArithmeticOutcome::equal(ArithmeticOutcome[T], ArithmeticOutcome[T]) -> Bool
pub fn ArithmeticError::equal(ArithmeticError, ArithmeticError) -> Bool
pub fn ArithmeticErrorKind::equal(ArithmeticErrorKind, ArithmeticErrorKind) -> Bool
pub fn CertificationStage::equal(CertificationStage, CertificationStage) -> Bool
pub fn CertificationFailureReason::equal(CertificationFailureReason, CertificationFailureReason) -> Bool
pub fn CertificationFailureDetail::equal(CertificationFailureDetail, CertificationFailureDetail) -> Bool
等価性は構造的で、フィールドごとに比較する。演算子 == と != を優先すること。メソッド形式は equal を関数として渡すコードのためにある。ArithmeticOutcome::equal は値を T の Eq で比較するので、Double では NaN を含む二つの結果は等しくならない。
非検査能力トレイト
各非検査トレイトは一つの能力であり、メソッドは Self を返す。トレイトは定義域を定めない。定義域の外で何が起きるか(NaN、無限大、中断、ラップアラウンド)はインスタンスが決める。すべて pub(open) なので、独自の型に実装できる。
Sqrt
Sqrt は平方根を提供する。
pub(open) trait Sqrt {
fn sqrt(Self) -> Self
}
Float と Double では sqrt は IEEE 754 に従い、負の入力には NaN を返し、 である。
fn[T : Add + Mul + @lf_arith.Sqrt] hypot_naive(x : T, y : T) -> T {
@lf_arith.Sqrt::sqrt(x * x + y * y)
}
test "generic hypotenuse" {
inspect(hypot_naive(3.0, 4.0), content="5")
inspect(hypot_naive((5.0 : Float), (12.0 : Float)), content="13")
}
Cbrt
Cbrt は実数の立方根を提供する。
pub(open) trait Cbrt {
fn cbrt(Self) -> Self
}
Float と Double では立方根は実数直線全体で実数値をとるので、 である。
Radical
Radical は連言 Sqrt + Cbrt であり、独自のメソッドは持たない。
pub(open) trait Radical : Sqrt + Cbrt {
}
fn[T : @lf_arith.Radical] roots(x : T) -> (T, T) {
(@lf_arith.Sqrt::sqrt(x), @lf_arith.Cbrt::cbrt(x))
}
test "radical" {
let (s, c) = roots(64.0)
inspect(s, content="8")
inspect(c, content="4")
inspect(@lf_arith.Cbrt::cbrt(-8.0), content="-2")
}
Exponential
Exponential は (exp)と (exp2)を提供する。
pub(open) trait Exponential {
fn exp(Self) -> Self
fn exp2(Self) -> Self
}
Logarithmic
Logarithmic は自然対数(ln)、二進対数(log2)、常用対数(log10)を提供する。
pub(open) trait Logarithmic {
fn ln(Self) -> Self
fn log2(Self) -> Self
fn log10(Self) -> Self
}
Float と Double では、負の引数は NaN を、ゼロは を与える。
test "exponentials and logarithms" {
inspect(@lf_arith.Exponential::exp(0.0), content="1")
inspect(@lf_arith.Exponential::exp2(10.0), content="1024")
inspect(@lf_arith.Logarithmic::log2(1024.0), content="10")
inspect(@lf_arith.Logarithmic::log10(1000.0), content="3")
inspect(@lf_arith.Logarithmic::ln(1.0), content="0")
}
Power
Power は底を同じ型の指数でべき乗する。
pub(open) trait Power {
fn pow(Self, Self) -> Self
}
| インスタンス | 意味 |
|---|---|
Float, Double | C の pow 関数:実数のべき乗。負の底と非整数の指数では NaN |
Int, Int16, Int64 | での正確なべき乗(オーバーフロー時はラップアラウンド)。負の指数では中断する |
UInt, UInt16, UInt64 | での正確なべき乗(オーバーフロー時はラップアラウンド) |
BigInt | 正確なべき乗。負の指数では中断する |
整数インスタンスは二進べき乗法を使い、乗算は 回である。どの底でも であり、 も含む。
test "power" {
inspect(@lf_arith.Power::pow(2, 10), content="1024")
inspect(@lf_arith.Power::pow(0, 0), content="1")
inspect(@lf_arith.Power::pow(2U, 32U), content="0")
inspect(@lf_arith.Power::pow(2.0, 0.5), content="1.4142135623730951")
inspect(@lf_arith.Power::pow(10N, 20N), content="100000000000000000000")
}
Trigonometric
Trigonometric は角度(提供されるインスタンスではラジアン)の sin、cos、tan を提供する。
pub(open) trait Trigonometric {
fn sin(Self) -> Self
fn cos(Self) -> Self
fn tan(Self) -> Self
}
InverseTrigonometric
InverseTrigonometric は主枝 asin、acos、atan と二引数の atan2(y, x) を提供する。
pub(open) trait InverseTrigonometric {
fn asin(Self) -> Self
fn acos(Self) -> Self
fn atan(Self) -> Self
fn atan2(Self, Self) -> Self
}
Float と Double での値域は、asin と atan が 、acos が 、atan2 が である。後者は点 の偏角を返し、負の 軸上ではゼロである の符号によって と のどちらかが選ばれる。
test "trigonometry" {
inspect(@lf_arith.Trigonometric::sin(0.0), content="0")
inspect(@lf_arith.Trigonometric::cos(0.0), content="1")
let pi : Double = @lf_arith.Constants::pi()
inspect(@lf_arith.InverseTrigonometric::atan2(0.0, -1.0) == pi, content="true")
inspect(@lf_arith.InverseTrigonometric::acos(1.0), content="0")
}
Hyperbolic
Hyperbolic は sinh、cosh、tanh を提供する。
pub(open) trait Hyperbolic {
fn sinh(Self) -> Self
fn cosh(Self) -> Self
fn tanh(Self) -> Self
}
InverseHyperbolic
InverseHyperbolic は asinh、acosh、atanh を提供する。
pub(open) trait InverseHyperbolic {
fn asinh(Self) -> Self
fn acosh(Self) -> Self
fn atanh(Self) -> Self
}
Float と Double では、acosh は で、atanh は で定義される。その外では結果は NaN であり、 である。
test "hyperbolic" {
inspect(@lf_arith.Hyperbolic::cosh(0.0), content="1")
inspect(@lf_arith.Hyperbolic::tanh(0.0), content="0")
inspect(@lf_arith.InverseHyperbolic::acosh(1.0), content="0")
inspect(@lf_arith.InverseHyperbolic::atanh(2.0).is_nan(), content="true")
}
Constants
Constants は Self における 、、 を提供する。
pub(open) trait Constants {
fn pi() -> Self
fn tau() -> Self
fn e() -> Self
}
これらのメソッドは引数をとらないので、目的の型が分かる形で呼び出すこと。Double では pi は @math.PI、tau は 2.0 * @math.PI(二倍は指数を変えるだけなので正確)、e は exp(1.0) である。Float では Double の値を Float へ丸めたものである。
test "constants" {
let pi : Double = @lf_arith.Constants::pi()
let tau : Double = @lf_arith.Constants::tau()
let e : Float = @lf_arith.Constants::e()
inspect(tau == 2.0 * pi, content="true")
inspect(pi, content="3.141592653589793")
inspect(e > (2.71 : Float), content="true")
}
検査付き能力トレイト
検査付きトレイトは Result[_, ArithmeticError] を返すので、拒否された演算は呼び出し側が処理しなければならない値になる。精度に依存しうるメソッドは ArithmeticContext を受け取る。組み込みの Float と Double のインスタンスはそれを受け取るが、読みはしない。
SqrtChecked
SqrtChecked は平方根を計算するか、定義域エラーを報告する。
pub(open) trait SqrtChecked {
fn sqrt_checked(Self, ArithmeticContext) -> Result[Self, ArithmeticError]
}
定義域は各インスタンスが定め、トレイトは順序を仮定しない。Float と Double では、引数 ( を含む)は DomainError になり、、、NaN はそのまま Sqrt::sqrt に渡される。
test "checked square root" {
let ctx = @lf_arith.ArithmeticContext::decimal64()
inspect(@lf_arith.SqrtChecked::sqrt_checked(2.25, ctx).unwrap(), content="1.5")
guard @lf_arith.SqrtChecked::sqrt_checked(-1.0, ctx) is Err(err) else {
fail("expected a domain error")
}
inspect(err.is_domain_error(), content="true")
inspect(err.message, content="square root is undefined for negative real inputs")
}
DivChecked
DivChecked は除算を行うか、商が定義されない理由を報告する。
pub(open) trait DivChecked {
fn div_checked(Self, Self, ArithmeticContext) -> Result[Self, ArithmeticError]
}
Float と Double では、検査は次の順に行われる:
| 場合 | 戻り値 |
|---|---|
DomainError (zero divided by zero is undefined) | |
DomainError (infinity divided by infinity is undefined) | |
| それ以外の (NaN を含む) | DivisionByZero (division by zero) |
| それ以外 | Ok(x / y)。NaN は IEEE どおり伝播する |
test "checked division" {
let ctx = @lf_arith.ArithmeticContext::decimal64()
inspect(@lf_arith.DivChecked::div_checked(10.0, 4.0, ctx).unwrap(), content="2.5")
let zero_by_zero = @lf_arith.DivChecked::div_checked(0.0, 0.0, ctx)
inspect(zero_by_zero is Err(e) && e.is_domain_error(), content="true")
let one_by_zero = @lf_arith.DivChecked::div_checked(1.0, -0.0, ctx)
inspect(one_by_zero is Err(e) && e.is_division_by_zero(), content="true")
}
CompareChecked
CompareChecked は二つの値を比較するか、順序付けられないことを報告する。
pub(open) trait CompareChecked {
fn compare_checked(Self, Self) -> Result[Int, ArithmeticError]
}
順序付けられる入力では結果は -1、0、1 のいずれかである。Float と Double では、NaN のオペランドは UnorderedComparison になり、 と は等しいと比較される。
test "checked comparison" {
inspect(@lf_arith.CompareChecked::compare_checked(1.0, 2.0).unwrap(), content="-1")
inspect(@lf_arith.CompareChecked::compare_checked(-0.0, 0.0).unwrap(), content="0")
let nan = 0.0 / 0.0
let r = @lf_arith.CompareChecked::compare_checked(nan, 1.0)
inspect(r is Err(e) && e.is_unordered_comparison(), content="true")
}
PowNatChecked
PowNatChecked は値を非負整数乗する。
pub(open) trait PowNatChecked {
fn pow_nat_checked(Self, UInt, ArithmeticContext) -> Result[Self, ArithmeticError]
}
は乗法単位元であり、 も含む。Float と Double のインスタンスは失敗しない。丸めを伴う乗算が高々 回の二進べき乗法を使い、通常の IEEE の積と同様に へオーバーフローし、ゼロへアンダーフローする。丸め誤差の上界は設計ページで示す。
PowIntChecked
PowIntChecked は値を符号付き整数乗する。
pub(open) trait PowIntChecked {
fn pow_int_checked(Self, Int, ArithmeticContext) -> Result[Self, ArithmeticError]
}
負の指数は逆数を意味する。ゼロの底と負の指数は、エラーとして(包含区間型なら文書化された包含区間として)報告しなければならず、黙って不正な値を返してはならない。Float と Double では:
- ;
- の は
pow_nat_checked(x, n)である。 - は のとき
DivisionByZeroであり、それ以外はdiv_checked(1, x^n)である。最も小さい負のIntの指数も、符号反転のオーバーフローなしに扱われる。
test "checked integer powers" {
let ctx = @lf_arith.ArithmeticContext::decimal64()
inspect(@lf_arith.PowNatChecked::pow_nat_checked(3.0, 4U, ctx).unwrap(), content="81")
inspect(@lf_arith.PowNatChecked::pow_nat_checked(0.0, 0U, ctx).unwrap(), content="1")
inspect(@lf_arith.PowIntChecked::pow_int_checked(2.0, -3, ctx).unwrap(), content="0.125")
let r = @lf_arith.PowIntChecked::pow_int_checked(0.0, -1, ctx)
inspect(r is Err(e) && e.is_division_by_zero(), content="true")
}
ParseChecked
ParseChecked はコンテキストのもとでテキストを Self へ解析する。
pub(open) trait ParseChecked {
fn parse_checked(String, ArithmeticContext) -> Result[Self, ArithmeticError]
}
コンテキストにより、十進バックエンドは解析した値を自身の精度へ丸められる。失敗には ParseError を使い、テキストは正しい形式だが形式がそれを保持できない場合は FormatError を使う。このパッケージはインスタンスを提供しない。解析は、十進型のようにテキスト形式を持つバックエンドの役割である。
コンテキスト付き能力トレイト
コンテキスト付きトレイトは Result[ArithmeticOutcome[Self], ArithmeticError] を返す。Err は演算が拒否されたことを意味し、Ok は値と、それを生成する間にバックエンドが検出した診断を運ぶ。
AddContextual, SubContextual, MulContextual, DivContextual
これら四つのトレイトは +、-、*、/ のコンテキスト付き版である。
pub(open) trait AddContextual {
fn add_contextual(Self, Self, ArithmeticContext) -> Result[ArithmeticOutcome[Self], ArithmeticError]
}
pub(open) trait SubContextual {
fn sub_contextual(Self, Self, ArithmeticContext) -> Result[ArithmeticOutcome[Self], ArithmeticError]
}
pub(open) trait MulContextual {
fn mul_contextual(Self, Self, ArithmeticContext) -> Result[ArithmeticOutcome[Self], ArithmeticError]
}
pub(open) trait DivContextual {
fn div_contextual(Self, Self, ArithmeticContext) -> Result[ArithmeticOutcome[Self], ArithmeticError]
}
コンテキストに忠実なバックエンドは、正確な結果をコンテキストのもとで丸めた を返し、その丸めが引き起こした診断を設定する。Float と Double では加算、減算、乗算は失敗せず、div_contextual は DivChecked::div_checked が失敗するときにちょうど失敗する。
test "contextual arithmetic" {
let ctx = @lf_arith.ArithmeticContext::decimal64()
let q = @lf_arith.DivContextual::div_contextual(10.0, 4.0, ctx).unwrap()
inspect(q.value, content="2.5")
let s = @lf_arith.AddContextual::add_contextual(0.1, 0.2, ctx).unwrap()
inspect(s.value, content="0.30000000000000004")
inspect(s.diagnostics.inexact, content="false")
inspect(@lf_arith.DivContextual::div_contextual(1.0, 0.0, ctx) is Err(_), content="true")
}
AbsContextual
AbsContextual はコンテキスト付きの絶対値である。
pub(open) trait AbsContextual {
fn abs_contextual(Self, ArithmeticContext) -> Result[ArithmeticOutcome[Self], ArithmeticError]
}
コンテキストの精度がオペランドより低いとき、バックエンドは丸めてよい。Float と Double では正確な IEEE の abs である。
SqrtContextual
SqrtContextual はコンテキスト付きの平方根である。
pub(open) trait SqrtContextual {
fn sqrt_contextual(Self, ArithmeticContext) -> Result[ArithmeticOutcome[Self], ArithmeticError]
}
Float と Double では、SqrtChecked::sqrt_checked が失敗するときにちょうど失敗する。
ExpContextual
ExpContextual はコンテキスト付きの自然指数関数である。
pub(open) trait ExpContextual {
fn exp_contextual(Self, ArithmeticContext) -> Result[ArithmeticOutcome[Self], ArithmeticError]
}
Float と Double では、Exponential::exp を正確な結果として包んだものである。
test "contextual sqrt, abs and exp" {
let ctx = @lf_arith.ArithmeticContext::decimal64()
inspect(@lf_arith.SqrtContextual::sqrt_contextual(9.0, ctx).unwrap().value, content="3")
inspect(@lf_arith.SqrtContextual::sqrt_contextual(-9.0, ctx) is Err(_), content="true")
inspect(@lf_arith.AbsContextual::abs_contextual(-2.0, ctx).unwrap().value, content="2")
inspect(@lf_arith.ExpContextual::exp_contextual(0.0, ctx).unwrap().value, content="1")
}
IntegralContextual
IntegralContextual はコンテキストのもとで MoonBit の Int を Self へ埋め込む。
pub(open) trait IntegralContextual {
fn from_int_contextual(Int, ArithmeticContext) -> Result[ArithmeticOutcome[Self], ArithmeticError]
}
入力は常に Int であり、より広い整数や任意精度の入力には別の能力が必要である。バックエンドはサポートしないコンテキストを拒否してよい。Double ではすべての Int が正確に表される。Float では、 でその大きさにおける間隔の倍数でない整数は丸められ、そのとき結果には inexact と rounded が設定される。
test "embed integers" {
let ctx = @lf_arith.ArithmeticContext::new(24)
let exact : @lf_arith.ArithmeticOutcome[Float] = @lf_arith.IntegralContextual::from_int_contextual(
16_777_216, ctx,
).unwrap()
inspect(exact.diagnostics.inexact, content="false")
let rounded : @lf_arith.ArithmeticOutcome[Float] = @lf_arith.IntegralContextual::from_int_contextual(
16_777_217, ctx,
).unwrap()
inspect(rounded.value, content="16777216")
inspect(rounded.diagnostics.inexact && rounded.diagnostics.rounded, content="true")
}
AdjacentContextual
AdjacentContextual は隣接する表現可能な値へ進む。
pub(open) trait AdjacentContextual {
fn next_plus_contextual(Self, ArithmeticContext) -> Result[ArithmeticOutcome[Self], ArithmeticError]
fn next_minus_contextual(Self, ArithmeticContext) -> Result[ArithmeticOutcome[Self], ArithmeticError]
fn next_toward_contextual(Self, Self, ArithmeticContext) -> Result[ArithmeticOutcome[Self], ArithmeticError]
}
next_plus_contextual(x) は より大きい最小の表現可能な値、next_minus_contextual(x) は より小さい最大の表現可能な値である。next_toward_contextual(x, t) は から の方向へ一歩進む。 のときは を返すので、next_toward(0.0, -0.0) は である。表現可能な集合がコンテキストに依存するバックエンドは、範囲に関する条件を診断で報告する。
Float と Double では、表現可能な集合は固定された IEEE 二進形式であり、結果は常に正確である:
| 入力 | next_plus | next_minus |
|---|---|---|
| 最小の正の非正規化数 | 最小の負の非正規化数(絶対値が最小) | |
| 最大の有限値 | 直前の値 | |
| 最大の有限値 | ||
| NaN | NaN | NaN |
どちらかのオペランドが NaN なら、next_toward は NaN を返す。
test "adjacent doubles" {
let ctx = @lf_arith.ArithmeticContext::new(53)
let up = @lf_arith.AdjacentContextual::next_plus_contextual(1.0, ctx).unwrap()
inspect(up.value - 1.0, content="2.220446049250313e-16")
let tiny = @lf_arith.AdjacentContextual::next_plus_contextual(0.0, ctx).unwrap()
inspect(tiny.value.reinterpret_as_uint64(), content="1")
let down = @lf_arith.AdjacentContextual::next_toward_contextual(1.0, 0.0, ctx).unwrap()
inspect(1.0 - down.value, content="1.1102230246251565e-16")
}
ConstantsContextual
ConstantsContextual はコンテキストのもとで 、、 を生成する。
pub(open) trait ConstantsContextual {
fn pi_contextual(ArithmeticContext) -> Result[ArithmeticOutcome[Self], ArithmeticError]
fn tau_contextual(ArithmeticContext) -> Result[ArithmeticOutcome[Self], ArithmeticError]
fn e_contextual(ArithmeticContext) -> Result[ArithmeticOutcome[Self], ArithmeticError]
}
定数がコンテキストに従い、診断が意味を持つ場合にだけ実装すること。証明付きバックエンドは、目標の丸めを証明できないとき CertificationFailure エラーを返す。Float と Double はこれを実装しない。設計ページを参照。
HyperbolicContextual
HyperbolicContextual はコンテキストのもとで sinh、cosh、tanh を提供する。
pub(open) trait HyperbolicContextual {
fn sinh_contextual(Self, ArithmeticContext) -> Result[ArithmeticOutcome[Self], ArithmeticError]
fn cosh_contextual(Self, ArithmeticContext) -> Result[ArithmeticOutcome[Self], ArithmeticError]
fn tanh_contextual(Self, ArithmeticContext) -> Result[ArithmeticOutcome[Self], ArithmeticError]
}
ConstantsContextual と同じ規則が適用され、Float と Double はこれも実装しない。
NumericFormatContextual
NumericFormatContextual は、コンテキストのもとでの Self の数値形式、すなわち特別な値と値のクラスを記述する。
pub(open) trait NumericFormatContextual {
fn zero_contextual(ArithmeticContext) -> Self
fn one_contextual(ArithmeticContext) -> Self
fn epsilon_contextual(ArithmeticContext) -> Self
fn min_normal_contextual(ArithmeticContext) -> Self
fn max_finite_contextual(ArithmeticContext) -> Self
fn classify_contextual(Self) -> FpClass
}
epsilon_contextual はマシンイプシロン 、つまり からその次に大きい表現可能な値までの距離である。最近接丸めの単位丸め誤差は である。これらのメソッドは失敗しない。固定形式ではコンテキストは無視される:
| メソッド | Float(binary32) | Double(binary64) |
|---|---|---|
epsilon_contextual | ||
min_normal_contextual | ||
max_finite_contextual |
test "format constants" {
let ctx = @lf_arith.ArithmeticContext::new(53)
let eps : Double = @lf_arith.NumericFormatContextual::epsilon_contextual(ctx)
inspect(eps, content="2.220446049250313e-16")
let one : Double = @lf_arith.NumericFormatContextual::one_contextual(ctx)
inspect(one + eps > one, content="true")
inspect(one + eps / 2.0 == one, content="true")
}
包含区間の関係
包含区間(enclosure)とは、ある集合(区間やボールなど)に属すると分かっている未知の実数を表す値である。これら五つのトレイトは二つの包含区間 と を関係付ける。これらは順序ではなく関係である。重なり合う包含区間では definitely_lt(X, Y) と definitely_lt(Y, X) の両方が偽になる。これらは点の組 、 のすべてについての主張として読むこと。このパッケージはインスタンスを提供せず、区間やボールのバックエンドが実装する。下記の区間についての式は設計ページで導出する。
| トレイト | 成り立つ条件 | 区間 、 の場合 |
|---|---|---|
Contains | かつ | |
Overlaps | かつ | |
DefinitelyLt | すべての 、 について | |
DefinitelyLe | すべての 、 について | |
MaybeEq | ある 、 について | かつ |
Contains
Contains は一つ目の包含区間が二つ目を含むかどうかを判定する。
pub(open) trait Contains {
fn contains(Self, Self) -> Bool
}
Overlaps
Overlaps は二つの包含区間が共通の点を持つかどうかを判定する。
pub(open) trait Overlaps {
fn overlaps(Self, Self) -> Bool
}
DefinitelyLt
DefinitelyLt は一つ目の包含区間のすべての点が二つ目のすべての点より小さいかどうかを判定する。
pub(open) trait DefinitelyLt {
fn definitely_lt(Self, Self) -> Bool
}
DefinitelyLe
DefinitelyLe は一つ目の包含区間のすべての点が二つ目のすべての点以下かどうかを判定する。
pub(open) trait DefinitelyLe {
fn definitely_le(Self, Self) -> Bool
}
MaybeEq
MaybeEq は二つの包含区間が同じ数を表しうるかどうかを判定する。
pub(open) trait MaybeEq {
fn maybe_eq(Self, Self) -> Bool
}
実数の包含区間では maybe_eq は overlaps と一致する。このトレイトは、汎用コードが意図した問いをそのまま書けるようにするために存在する。
struct Iv {
lo : Double
hi : Double
}
impl @lf_arith.Contains for Iv with contains(x, y) { x.lo <= y.lo && y.hi <= x.hi }
impl @lf_arith.DefinitelyLt for Iv with definitely_lt(x, y) { x.hi < y.lo }
impl @lf_arith.DefinitelyLe for Iv with definitely_le(x, y) { x.hi <= y.lo }
impl @lf_arith.MaybeEq for Iv with maybe_eq(x, y) { x.lo <= y.hi && y.lo <= x.hi }
test "interval relations" {
let x = Iv::{ lo: 1.0, hi: 2.0 }
let y = Iv::{ lo: 1.5, hi: 3.0 }
let z = Iv::{ lo: 2.5, hi: 3.0 }
inspect(@lf_arith.DefinitelyLt::definitely_lt(x, z), content="true")
inspect(@lf_arith.DefinitelyLt::definitely_lt(x, y), content="false")
inspect(@lf_arith.DefinitelyLt::definitely_lt(y, x), content="false")
inspect(@lf_arith.MaybeEq::maybe_eq(x, y), content="true")
inspect(@lf_arith.Contains::contains(y, z), content="true")
inspect(@lf_arith.DefinitelyLe::definitely_le(x, Iv::{ lo: 2.0, hi: 4.0 }), content="true")
}
提供されるインスタンス
| トレイト | Float, Double | 整数と BigInt |
|---|---|---|
Sqrt, Cbrt, Radical, Exponential, Logarithmic, Trigonometric, InverseTrigonometric, Hyperbolic, InverseHyperbolic, Constants | あり | なし |
Power | あり | Int, Int16, Int64, UInt, UInt16, UInt64, BigInt |
SqrtChecked, DivChecked, CompareChecked, PowNatChecked, PowIntChecked | あり | なし |
ParseChecked | なし | なし |
AddContextual, SubContextual, MulContextual, DivContextual, AbsContextual, SqrtContextual, ExpContextual, IntegralContextual, AdjacentContextual, NumericFormatContextual | あり | なし |
ConstantsContextual, HyperbolicContextual | なし | なし |
Contains, Overlaps, DefinitelyLt, DefinitelyLe, MaybeEq | なし | なし |
Float と Double の初等関数は Kaida-Amethyst/math から来ており、正しい丸めは保証されない。
非推奨
MoonBit 0.10 より前は、型にトレイトを実装すると、そのメソッドが暗黙にドット記法で呼べるようになっていた。これらの暗黙のメソッド形式はパッケージの公開型で今も呼べるが、非推奨であり、インターフェースファイルからは隠されている:
| 非推奨の形式 | 型 | 代替 |
|---|---|---|
x.not_equal(y) | 等価性に挙げたすべての型 | x != y |
x.to_repr() | FpClass, ArithmeticDiagnostics, ArithmeticOutcome | Repr(x) または @debug.to_string(x) |