semantic API

semantic 将 floating 的具体值投影到一个与表示无关的小型模型上:精确的既约有理数、带符号的无穷、NaN、由此类标量构成的闭区间,以及一套简短的错误词汇。两个不同表示的值(例如 binary64 的 BinFloat 与 IEEE 的 Decimal)当且仅当表示同一个数时,投影为相等的语义值。本包不执行任何算术运算和舍入。教程 展示了典型的比较;设计页面 推导了该投影为何是精确的以及它会丢弃哪些信息。

示例假定使用以下辅助函数,它将语义标量打印为 numerator/denominator:

///|
fn show(s : @semantic.SemanticScalar) -> String {
  match s {
    Rational(q) => q.numerator().to_string() + "/" + q.denominator().to_string()
    Infinity(@def.Sign::Negative) => "-inf"
    Infinity(_) => "+inf"
    NaN => "nan"
  }
}

精确有理数

ExactRational

ExactRational 是以最简形式保存、分母为正的精确有理数。

pub struct ExactRational {
  // private fields
} derive(Eq)

其字段是私有的,因此每个值都由 ExactRational::new 或 ExactRational::from_scaled_integer 构建,并满足不变式

nd  with  d>0,gcd⁡(∣n∣,d)=1,n=0  ⟹  d=1.\frac{n}{d}\ \text{ with }\ d > 0,\quad \gcd(|n|, d) = 1,\quad n = 0 \implies d = 1.

由于每个有理数的这种形式都是唯一的,派生的 == 就是有理数的相等。

ExactRational::new

new(numerator, denominator) 构建有理数 n/dn/d 并将其约简。

pub fn ExactRational::new(@bigint.BigInt, @bigint.BigInt) -> Self

负分母的符号会移到分子上;分子分母同除以它们的最大公约数(在 BigInt 上使用欧几里得算法);零变为 0/10/1。当 denominator 为零时中止。

ExactRational::from_scaled_integer

from_scaled_integer(significand, radix, exponent) 构建精确值 m⋅rem \cdot r^{e}。

pub fn ExactRational::from_scaled_integer(@bigint.BigInt, Int, Int) -> Self

对于 e≥0e \ge 0,结果为 (mre)/1(m r^{e})/1;对于 e<0e < 0,结果为约简后的 m/r−em / r^{-e}。当 radix ≤1\le 1 时中止。幂是精确计算的,因此结果的大小随 ∣e∣|e| 线性增长(约 ∣e∣log⁡2r|e| \log_2 r 位)。

ExactRational::numerator, ExactRational::denominator

这些访问器返回约简后的分子(带符号)和正的分母。

pub fn ExactRational::numerator(Self) -> @bigint.BigInt
pub fn ExactRational::denominator(Self) -> @bigint.BigInt
///|
test "exact rationals are reduced" {
  let q = @semantic.ExactRational::new(6N, -8N)
  inspect(q.numerator().to_string() + "/" + q.denominator().to_string(), content="-3/4")
  let scaled = @semantic.ExactRational::from_scaled_integer(-12N, 10, -3)
  inspect(
    scaled.numerator().to_string() + "/" + scaled.denominator().to_string(),
    content="-3/250",
  )
  inspect(
    @semantic.ExactRational::from_scaled_integer(3N, 2, 4) ==
    @semantic.ExactRational::new(48N, 1N),
    content="true",
  )
}

标量

SemanticScalar

SemanticScalar 表示一个浮点数据的含义。

pub(all) enum SemanticScalar {
  Rational(ExactRational)
  Infinity(@def.Sign)
  NaN
} derive(Eq)

Rational 容纳每个有限值,两种带符号零都表示为 0/10/1。Infinity 携带 Negative 或 Positive。NaN 代表所有 NaN:静默或信号、任意载荷、任意符号。相等是结构相等,因此这里 NaN == NaN 为 true,与 IEEE 比较不同。

SemanticScalar::from_bin_float

from_bin_float(x) 返回 BinFloat 的精确含义。

pub fn SemanticScalar::from_bin_float(@bin_float.BinFloat) -> Self

有限的 x=(−1)sc 2ex = (-1)^{s} c\, 2^{e} 映射为 from_scaled_integer(±c, 2, e) 的 Rational;无穷映射为 Infinity(x.sign());NaN 映射为 NaN。精度被丢弃。从不中止。

SemanticScalar::from_decimal

from_decimal(x) 返回 IEEE @decimal.Decimal 的精确含义。

pub fn SemanticScalar::from_decimal(@decimal.Decimal) -> Self

有限的 x=(−1)sc 10qx = (-1)^{s} c\, 10^{q} 映射为 from_scaled_integer(±c, 10, q) 的 Rational,因此同值类的每个成员(1.5、1.50、1.500)都映射为同一个值 3/23/2。无穷与 NaN 的映射方式与二进制相同。GDA 类型 @decimal_gda.Decimal 在本包中没有投影。

///|
test "project binary and decimal values" {
  let binary = @semantic.SemanticScalar::from_bin_float(
    @bin_float.BinFloat::from_double(0.1),
  )
  inspect(show(binary), content="3602879701896397/36028797018963968")
  let decimal = @semantic.SemanticScalar::from_decimal(
    @decimal.Decimal::from_string("0.1").unwrap(),
  )
  inspect(show(decimal), content="1/10")
  inspect(binary == decimal, content="false")
  let half = @semantic.SemanticScalar::from_bin_float(
    @bin_float.BinFloat::from_double(0.5),
  )
  let half_decimal = @semantic.SemanticScalar::from_decimal(
    @decimal.Decimal::from_string("0.500").unwrap(),
  )
  inspect(half == half_decimal, content="true")
  inspect(
    show(@semantic.SemanticScalar::from_bin_float(@bin_float.BinFloat::from_double(-0.0))),
    content="0/1",
  )
  inspect(
    @semantic.SemanticScalar::from_bin_float(@bin_float.BinFloat::nan()) ==
    @semantic.SemanticScalar::NaN,
    content="true",
  )
}

区间

SemanticInterval

SemanticInterval 是由两个语义端点给出的闭区间。

pub struct SemanticInterval {
  lower : SemanticScalar
  upper : SemanticScalar
} derive(Eq)

其字段是公开的,在包外只读。该类型不检查 lower 是否不超过 upper;下文的投影使用反序对 (+∞,−∞)(+\infty, -\infty) 表示空集。

SemanticInterval::from_ball_float

from_ball_float(x) 投影 BallFloat 的两个端点。

pub fn SemanticInterval::from_ball_float(@ball_float.BallFloat) -> Self

lower 为 from_bin_float(x.lower_bound()),upper 为 from_bin_float(x.upper_bound())。有界区间给出两个 Rational 端点,无界的一侧给出 Infinity,整条实数线给出 (−∞,+∞)(-\infty, +\infty),空区间给出 (Infinity(Positive),Infinity(Negative))(\texttt{Infinity(Positive)}, \texttt{Infinity(Negative)})。装饰、精度和标志均被丢弃。

///|
test "project intervals" {
  let x = @semantic.SemanticInterval::from_ball_float(
    @ball_float.BallFloat::from_bounds(
      @bin_float.BinFloat::from_int(1),
      @bin_float.BinFloat::from_double(2.5),
    ),
  )
  inspect(show(x.lower) + " .. " + show(x.upper), content="1/1 .. 5/2")
  let whole = @semantic.SemanticInterval::from_ball_float(
    @ball_float.BallFloat::whole(),
  )
  inspect(show(whole.lower) + " .. " + show(whole.upper), content="-inf .. +inf")
  let empty = @semantic.SemanticInterval::from_ball_float(
    @ball_float.BallFloat::empty(),
  )
  inspect(show(empty.lower) + " .. " + show(empty.upper), content="+inf .. -inf")
}

错误与 checked 结果

SemanticError

SemanticError 是与表示无关的错误词汇。

pub(all) enum SemanticError {
  DivisionByZero
  ParseError
  DomainError
  FormatError
  UnsupportedOperation
  UnorderedComparison
  CertificationFailure
} derive(Eq)

它对应 Luna-Flow/arithmetic 的 ArithmeticErrorKind,但不含消息,也不含认证细节。

SemanticError::from_arithmetic

from_arithmetic(err) 对 ArithmeticError 进行分类。

pub fn SemanticError::from_arithmetic(@arithmetic.ArithmeticError) -> Self

种类按以下顺序检测:除以零、解析、定义域、格式、无序比较、认证失败;其他任何情况(UnsupportedOperation 种类)映射为 UnsupportedOperation。消息与 CertificationFailureDetail 被丢弃。

SemanticResult

SemanticResult[T] 是一个语义值或一个语义错误。

pub(all) enum SemanticResult[T] {
  Value(T)
  Error(SemanticError)
} derive(Eq)

semantic_scalar_result, semantic_interval_result

这些函数将具体包的 checked 结果转换为语义结果,成功情形使用调用者提供的投影。

pub fn[T] semantic_scalar_result(Result[T, @arithmetic.ArithmeticError], (T) -> SemanticScalar) -> SemanticResult[SemanticScalar]
pub fn[T] semantic_interval_result(Result[T, @arithmetic.ArithmeticError], (T) -> SemanticInterval) -> SemanticResult[SemanticInterval]
semantic_scalar_result(Ok(v),f)=Value(f(v)),semantic_scalar_result(Err(e),f)=Error(from_arithmetic(e)),\begin{aligned} \texttt{semantic\_scalar\_result}(\texttt{Ok}(v), f) &= \texttt{Value}(f(v)),\\ \texttt{semantic\_scalar\_result}(\texttt{Err}(e), f) &= \texttt{Error}(\texttt{from\_arithmetic}(e)), \end{aligned}

区间同理。f 至多调用一次。

///|
test "checked results become semantic results" {
  let failed = @semantic.semantic_scalar_result(
    @bin_float.BinFloat::from_int(1).div_checked(@bin_float.BinFloat::zero()),
    @semantic.SemanticScalar::from_bin_float,
  )
  inspect(
    failed == @semantic.SemanticResult::Error(@semantic.SemanticError::DivisionByZero),
    content="true",
  )
  let root = @semantic.semantic_scalar_result(
    @bin_float.BinFloat::from_int(4).sqrt(),
    @semantic.SemanticScalar::from_bin_float,
  )
  let two = @semantic.SemanticScalar::from_decimal(@decimal.Decimal::from_int(2))
  inspect(root == @semantic.SemanticResult::Value(two), content="true")
}

trait 实现

相等

本包的每个类型都派生 Eq;equal 和 not_equal 方法经显式提升。请优先使用 == 和 !=。

pub fn ExactRational::equal(Self, Self) -> Bool
pub fn ExactRational::not_equal(Self, Self) -> Bool
pub fn SemanticScalar::equal(Self, Self) -> Bool
pub fn SemanticScalar::not_equal(Self, Self) -> Bool
pub fn SemanticInterval::equal(Self, Self) -> Bool
pub fn SemanticInterval::not_equal(Self, Self) -> Bool
pub fn SemanticError::equal(Self, Self) -> Bool
pub fn SemanticError::not_equal(Self, Self) -> Bool
pub fn[T : Eq] SemanticResult::equal(Self[T], Self[T]) -> Bool
pub fn[T : Eq] SemanticResult::not_equal(Self[T], Self[T]) -> Bool

由于采用约简形式,ExactRational 的相等即数值相等;SemanticScalar 的相等额外加入 NaN == NaN,并区分两种无穷。不提供序关系。

完整公共接口

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

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

import {
  "Luna-Flow/arithmetic",
  "Luna-Flow/floating/ball_float",
  "Luna-Flow/floating/bin_float",
  "Luna-Flow/floating/decimal",
  "Luna-Flow/floating/def",
  "moonbitlang/core/bigint",
}

// Values
pub fn[T] semantic_interval_result(Result[T, @arithmetic.ArithmeticError], (T) -> SemanticInterval) -> SemanticResult[SemanticInterval]

pub fn[T] semantic_scalar_result(Result[T, @arithmetic.ArithmeticError], (T) -> SemanticScalar) -> SemanticResult[SemanticScalar]

// Errors

// Types and methods
pub struct ExactRational {
  // private fields
} derive(Eq)
pub fn ExactRational::denominator(Self) -> @bigint.BigInt
pub fn ExactRational::equal(Self, Self) -> Bool
pub fn ExactRational::from_scaled_integer(@bigint.BigInt, Int, Int) -> Self
pub fn ExactRational::new(@bigint.BigInt, @bigint.BigInt) -> Self
pub fn ExactRational::not_equal(Self, Self) -> Bool
pub fn ExactRational::numerator(Self) -> @bigint.BigInt

pub(all) enum SemanticError {
  DivisionByZero
  ParseError
  DomainError
  FormatError
  UnsupportedOperation
  UnorderedComparison
  CertificationFailure
} derive(Eq)
pub fn SemanticError::equal(Self, Self) -> Bool
pub fn SemanticError::from_arithmetic(@arithmetic.ArithmeticError) -> Self
pub fn SemanticError::not_equal(Self, Self) -> Bool

pub struct SemanticInterval {
  lower : SemanticScalar
  upper : SemanticScalar
} derive(Eq)
pub fn SemanticInterval::equal(Self, Self) -> Bool
pub fn SemanticInterval::from_ball_float(@ball_float.BallFloat) -> Self
pub fn SemanticInterval::not_equal(Self, Self) -> Bool

pub(all) enum SemanticResult[T] {
  Value(T)
  Error(SemanticError)
} derive(Eq)
pub fn[T : Eq] SemanticResult::equal(Self[T], Self[T]) -> Bool
pub fn[T : Eq] SemanticResult::not_equal(Self[T], Self[T]) -> Bool

pub(all) enum SemanticScalar {
  Rational(ExactRational)
  Infinity(@def.Sign)
  NaN
} derive(Eq)
pub fn SemanticScalar::equal(Self, Self) -> Bool
pub fn SemanticScalar::from_bin_float(@bin_float.BinFloat) -> Self
pub fn SemanticScalar::from_decimal(@decimal.Decimal) -> Self
pub fn SemanticScalar::not_equal(Self, Self) -> Bool

// Type aliases

// Traits