def API

def 是 floating 各包的共享词汇。它定义了符号分类 Sign、四路比较结果 PartialOrder、本仓库中每种标量或区间表示都实现的开放 trait Floating、基于该 trait 构建的四个泛型谓词,并重新导出 Luna-Flow/arithmetic 中的上下文、舍入、分类和错误类型,使调用者只需一次导入。它不包含任何算术运算。教程 展示了这些词汇的用法;设计页面 陈述了 trait 定律,并解释了为何 PartialOrder 不是全序。

本页的每个示例都是一个能在当前分支上编译通过的完整测试。该包以 @def 导入;@lf_arith 即 Luna-Flow/arithmetic。

符号与比较结果

Sign

Sign 是 Floating::sign 返回的三路符号分类。

pub(all) enum Sign {
  Negative
  Zero
  Positive
} derive(Eq)

两种带符号零都返回 Zero,因此 Sign 不区分 −0-0 与 +0+0;当零的符号位很重要时,请使用具体的包(例如 BinFloat::is_negative_zero)。具体实现对 NaN 也返回 Zero,而区间在包含 00 时返回 Zero(参见 Floating::sign)。Sign 也是 semantic 包中 SemanticScalar::Infinity 的载荷,以及 BinFloat::inf 等构造函数的参数。

///|
test "Sign values" {
  let s = @def.Floating::sign(@bin_float.BinFloat::from_int(-3))
  inspect(s == @def.Sign::Negative, content="true")
  let z = @def.Floating::sign(@bin_float.BinFloat::from_double(-0.0))
  inspect(z == @def.Sign::Zero, content="true")
}

PartialOrder

PartialOrder 是 IEEE 754 比较的结果:两个浮点数据之间恰好成立 小于、等于、大于 和 无序 这四种互斥关系之一。

pub(all) enum PartialOrder {
  Less
  Equal
  Greater
  Unordered
} derive(Eq)

当且仅当至少一个操作数为 NaN 时 Unordered 成立;−0-0 和 +0+0 比较 Equal。该类型由 bin_float 中的 BinFloat::compare_quiet 和 BinFloat::compare_signaling 返回(各自附带该比较的标志)。它仅是一个值类型;def 本身不执行任何比较。为何是四种结果而非 Compare 实例,其推导见设计页面。

///|
test "PartialOrder from an IEEE comparison" {
  let one = @bin_float.BinFloat::from_int(1)
  let nan = @bin_float.BinFloat::nan()
  let (relation, flags) = one.compare_quiet(nan)
  inspect(relation == @def.PartialOrder::Unordered, content="true")
  inspect(flags.invalid_operation(), content="false")
  let (signaling, signaling_flags) = one.compare_signaling(nan)
  inspect(signaling == @def.PartialOrder::Unordered, content="true")
  inspect(signaling_flags.invalid_operation(), content="true")
  let zeros = @bin_float.BinFloat::from_double(-0.0).compare_quiet(
    @bin_float.BinFloat::zero(),
  )
  inspect(zeros.0 == @def.PartialOrder::Equal, content="true")
}

Floating trait

Floating 是本仓库中所有表示共享的观察与重设精度接口。

pub(open) trait Floating {
  fn classify(Self) -> @arithmetic.FpClass
  fn sign(Self) -> Sign
  fn precision(Self) -> Int
  fn with_precision(Self, Int, @arithmetic.RoundingMode) -> Self
  fn normalized(Self) -> Self
}

该 trait 为 pub(open),因此下游类型可以实现它。在本仓库中,@bin_float.BinFloat、@decimal.Decimal、@decimal_gda.Decimal 和 @ball_float.BallFloat 实现了它。自 MoonBit 0.10 迁移以来,trait 方法不再自动提升,因此泛型代码以限定形式 @def.Floating::classify(x) 调用它们;具体类型也提供同名的固有方法(x.classify())。每个实现应满足的定律列于设计页面。

Floating::classify

classify 返回值的类别:Finite、Infinity 或 NaN。

fn classify(Self) -> @arithmetic.FpClass

对于标量类型,这是将信号 NaN 与静默 NaN 合并后的 IEEE 类别。对于 BallFloat,有界非空区间为 Finite,带有无穷端点的区间(包括整条实数线)为 Infinity,空区间为 NaN。全函数;从不中止。

Floating::sign

sign 返回值的符号分类。

fn sign(Self) -> Sign
实现NegativeZeroPositive
BinFloat,两种 Decimal值 <0< 0,包括 −∞-\infty±0\pm 0 及所有 NaN值 >0> 0,包括 +∞+\infty
BallFloat [ℓ,u][\ell, u]u<0u < 0ℓ≤0≤u\ell \le 0 \le uℓ>0\ell > 0

BallFloat::sign 在空区间上会中止,因为空区间没有符号;请先检测 @def.is_nan(x)(当且仅当为空区间时为真)。

Floating::precision

precision 返回值上存储的工作精度:对 BinFloat 和 BallFloat 端点为有效二进制位数,对两种 Decimal 类型为有效十进制位数。结果总是至少为 11。

Floating::with_precision

with_precision(x, p, mode) 返回以精度 max⁡(1,p)\max(1, p) 重新表示的 x。

fn with_precision(Self, Int, @arithmetic.RoundingMode) -> Self

对于有限标量,值按给定方向舍入到其基数下的 max⁡(1,p)\max(1,p) 位有效数字,指数范围不受限制,因此从不上溢或下溢;不报告任何标志(如需标志,请使用具体包的 *_ctx API)。无穷与 NaN 保持其类别,仅改变所存储的精度。对于 BallFloat,结果是输入的包络:中心按 mode 舍入,舍入误差加到半径上,因此无论 mode 为何,x 的每个成员都仍是结果的成员。

Floating::normalized

normalized 返回值的规范代表,不改变其数学值。

fn normalized(Self) -> Self

对于 BinFloat,这是系数为奇数(或为零)的表示;对于两种 Decimal 类型,它去除系数末尾的零,因此 1.500 变为 1.5(同值类改变,值不变)。非有限值原样返回。normalized 是幂等的。

///|
fn[F : @def.Floating] describe(x : F) -> String {
  let class = match @def.Floating::classify(x) {
    Finite => "finite"
    Infinity => "infinite"
    NaN => "nan"
  }
  let sign = match @def.Floating::sign(x) {
    Negative => "-"
    Zero => "0"
    Positive => "+"
  }
  "\{class} \{sign} precision=\{@def.Floating::precision(x)}"
}

///|
test "Floating observations" {
  inspect(
    describe(@bin_float.BinFloat::from_double(-0.0)),
    content="finite 0 precision=53",
  )
  inspect(
    describe(@decimal.Decimal::from_string("-1.50").unwrap()),
    content="finite - precision=34",
  )
  inspect(
    describe(@decimal_gda.Decimal::from_string("-Inf").unwrap()),
    content="infinite - precision=34",
  )
  let around_zero = @ball_float.BallFloat::from_bounds(
    @bin_float.BinFloat::from_int(-1),
    @bin_float.BinFloat::from_int(1),
  )
  inspect(describe(around_zero), content="finite 0 precision=53")
}

///|
test "Floating re-precision and normalization" {
  let d = @decimal.Decimal::from_string("2.71828").unwrap()
  let cut = @def.Floating::with_precision(d, 3, @lf_arith.RoundingMode::TowardZero)
  inspect(cut.to_string(), content="2.71")
  let ten = @def.Floating::with_precision(
    @bin_float.BinFloat::from_int(10),
    2,
    @lf_arith.RoundingMode::ToNearestEven,
  )
  inspect(ten.to_string(), content="1p3")
  inspect(ten.precision(), content="2")
  let cohort = @decimal.Decimal::from_string("1.500").unwrap()
  inspect(@def.Floating::normalized(cohort).to_string(), content="1.5")
}

泛型谓词

is_finite, is_infinite, is_nan, is_zero

这些函数检测任意 Floating 值的类别。

pub fn[F : Floating] is_finite(F) -> Bool
pub fn[F : Floating] is_infinite(F) -> Bool
pub fn[F : Floating] is_nan(F) -> Bool
pub fn[F : Floating] is_zero(F) -> Bool
is_finite(x)  ⟺  classify(x)=Finite,is_infinite(x)  ⟺  classify(x)=Infinity,is_nan(x)  ⟺  classify(x)=NaN,is_zero(x)  ⟺  classify(x)=Finite∧sign(x)=Zero.\begin{aligned} \texttt{is\_finite}(x) &\iff \texttt{classify}(x) = \texttt{Finite},\\ \texttt{is\_infinite}(x) &\iff \texttt{classify}(x) = \texttt{Infinity},\\ \texttt{is\_nan}(x) &\iff \texttt{classify}(x) = \texttt{NaN},\\ \texttt{is\_zero}(x) &\iff \texttt{classify}(x) = \texttt{Finite} \wedge \texttt{sign}(x) = \texttt{Zero}. \end{aligned}

对每个值,前三者中恰有一个为真。对于标量,is_zero 对两种带符号零都为真。对于 BallFloat,is_zero 对每个包含零的有界区间(例如 [−1,1][-1, 1])都为真,而不仅限于 [0,0][0, 0];若你指的是其他含义,请使用 BallFloat::contains_zero 或比较区间端点。is_zero 从不在空区间上调用 sign,因为合取在 classify 处即短路停止。

///|
test "generic predicates" {
  inspect(@def.is_zero(@bin_float.BinFloat::from_double(-0.0)), content="true")
  inspect(
    @def.is_finite(@decimal.Decimal::from_string("NaN").unwrap()),
    content="false",
  )
  inspect(@def.is_nan(@ball_float.BallFloat::empty()), content="true")
  let around_zero = @ball_float.BallFloat::from_bounds(
    @bin_float.BinFloat::from_int(-1),
    @bin_float.BinFloat::from_int(1),
  )
  inspect(@def.is_zero(around_zero), content="true")
}

重新导出的类型

def 以 pub using 重新导出这些类型,因此 @def.RoundingMode 与 @lf_arith.RoundingMode 指同一类型。它们的定义和语义由 Luna-Flow/arithmetic 文档说明。

pub using @arithmetic {type ArithmeticContext}
pub using @arithmetic {type ArithmeticError}
pub using @arithmetic {type ArithmeticErrorKind}
pub using @bigint {type BigInt}
pub using @arithmetic {type CertificationFailureDetail}
pub using @arithmetic {type CertificationFailureReason}
pub using @arithmetic {type CertificationStage}
pub using @arithmetic {type FpClass}
pub using @arithmetic {type RoundingMode}
别名在 floating 中的作用
ArithmeticContext传递给 arithmetic 上下文 trait 的精度、舍入及可选指数界限;由各自的 from_arithmetic_context 构造函数映射为 BinaryContext / DecimalContext。
ArithmeticError, ArithmeticErrorKind所有返回 Result 的 API(*_checked、try_*)以及各 checked 包装包的结构化错误。
CertificationFailureDetail, CertificationFailureReason, CertificationStage种类为 CertificationFailure 的 ArithmeticError 的载荷:表示某个初等函数的正确舍入结果未能在其预算内完成认证。
FpClassFloating::classify 的结果。
RoundingModewith_precision 及各具体构造函数接受的五种舍入方向。
BigIntmoonbitlang/core/bigint 的任意精度整数,用于表示系数。
///|
test "aliases name the arithmetic types" {
  let ctx : @def.ArithmeticContext = @lf_arith.ArithmeticContext::new(10)
  inspect(ctx.precision, content="10")
  let mode : @def.RoundingMode = ToNearestEven
  inspect(mode == @lf_arith.RoundingMode::ToNearestEven, content="true")
}

trait 实现

Sign::equal, Sign::not_equal, PartialOrder::equal, PartialOrder::not_equal

这些方法是派生的 Eq 实例,经显式提升以使 a.equal(b) 仍然可用;请优先使用 == 和 !=。

pub fn Sign::equal(Self, Self) -> Bool
pub fn Sign::not_equal(Self, Self) -> Bool
pub fn PartialOrder::equal(Self, Self) -> Bool
pub fn PartialOrder::not_equal(Self, Self) -> Bool

完整公共接口

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

// Generated using `moon info`, DON'T EDIT IT
package "Luna-Flow/floating/def"

import {
  "Luna-Flow/arithmetic",
  "moonbitlang/core/bigint",
}

// Values
pub fn[F : Floating] is_finite(F) -> Bool

pub fn[F : Floating] is_infinite(F) -> Bool

pub fn[F : Floating] is_nan(F) -> Bool

pub fn[F : Floating] is_zero(F) -> Bool

// Errors

// Types and methods
pub(all) enum PartialOrder {
  Less
  Equal
  Greater
  Unordered
} derive(Eq)
pub fn PartialOrder::equal(Self, Self) -> Bool
pub fn PartialOrder::not_equal(Self, Self) -> Bool

pub(all) enum Sign {
  Negative
  Zero
  Positive
} derive(Eq)
pub fn Sign::equal(Self, Self) -> Bool
pub fn Sign::not_equal(Self, Self) -> Bool

// Type aliases
pub using @arithmetic {type ArithmeticContext}

pub using @arithmetic {type ArithmeticError}

pub using @arithmetic {type ArithmeticErrorKind}

pub using @bigint {type BigInt}

pub using @arithmetic {type CertificationFailureDetail}

pub using @arithmetic {type CertificationFailureReason}

pub using @arithmetic {type CertificationStage}

pub using @arithmetic {type FpClass}

pub using @arithmetic {type RoundingMode}

// Traits
pub(open) trait Floating {
  fn classify(Self) -> @arithmetic.FpClass
  fn sign(Self) -> Sign
  fn precision(Self) -> Int
  fn with_precision(Self, Int, @arithmetic.RoundingMode) -> Self
  fn normalized(Self) -> Self
}