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 而非 pub(all):可以对它做模式匹配,但错误需要通过下面的 ArithmeticError 构造函数来构造。
ArithmeticError
ArithmeticError 是所有检查 trait 和上下文 trait 返回的结构化错误。
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 值会使两个结果不相等。
非检查能力 trait
每个非检查 trait 都是一项能力,其方法返回 Self。trait 不规定定义域:定义域之外的行为(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)、以 2 为底的对数(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(结果精确,因为乘以 2 只改变指数),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")
}
检查能力 trait
检查 trait 返回 Result[_, ArithmeticError],因此被拒绝的运算是调用方必须处理的值。可能依赖精度的方法接受一个 ArithmeticContext;内置的 Float 和 Double 实例接受该参数但不读取它。
SqrtChecked
SqrtChecked 计算平方根,或报告定义域错误。
pub(open) trait SqrtChecked {
fn sqrt_checked(Self, ArithmeticContext) -> Result[Self, ArithmeticError]
}
定义域由各实例规定;trait 不假定存在序。对于 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),按 IEEE 规则传播 NaN |
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。本包不提供任何实例;解析属于具有文本格式的后端,例如十进制类型。
上下文能力 trait
上下文 trait 返回 Result[ArithmeticOutcome[Self], ArithmeticError]。Err 表示运算被拒绝;Ok 携带值以及后端在计算过程中检测到的诊断。
AddContextual, SubContextual, MulContextual, DivContextual
这四个 trait 是 +、-、* 和 / 的上下文形式。
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 是机器 epsilon ,即 到下一个更大可表示值的距离;就近舍入的单位舍入误差为 。这些方法不会失败。对于固定格式,上下文被忽略:
| 方法 | 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")
}
包络关系
包络是这样一种值:它代表一个未知实数,并已知该实数位于某个集合中,例如区间或球。这五个 trait 描述两个包络 与 之间的关系。它们是关系而不是序:对于相互重叠的包络,definitely_lt(X, Y) 和 definitely_lt(Y, X) 都为假。应把它们理解为关于每一对点 、 的陈述。本包不提供任何实例;由区间后端和球后端实现它们。设计页推导了下面的区间公式。
| Trait | 成立条件 | 对区间 、 |
|---|---|---|
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 一致;该 trait 的存在是为了让泛型代码能按其本意提问。
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")
}
内置实例
| Trait | 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 之前,为某个类型实现 trait 会隐式地使其方法可以用点语法调用。这些隐式方法形式在本包的公开类型上仍可调用,但已弃用,并且不出现在接口文件中:
| 弃用形式 | 类型 | 替代写法 |
|---|---|---|
x.not_equal(y) | 相等性一节列出的所有类型 | x != y |
x.to_repr() | FpClass, ArithmeticDiagnostics, ArithmeticOutcome | Repr(x) 或 @debug.to_string(x) |