快速上手

本指南带你从一个空的 MoonBit 项目开始,用 Luna-Flow/floating 得到正确的初步结果:选择包、安装、构造值、选择精度与失败的报告方式,以及解读结果。每个示例都能在当前分支上编译。

选择数值域

根据程序所需的语义来选择包,而不是根据输入的书写形式。

需求包主要类型结果
任意精度二进制值,IEEE 754 二进制格式bin_floatBinFloat值,或在 BinaryContext 下的 (value, BinaryFlags)
IEEE 754 十进制算术与 decimal32/64/128 交换格式decimalDecimal值,或在 DecimalContext 下的 (value, DecimalFlags)
带粘滞状态与陷阱的 General Decimal Arithmeticdecimal_gdaDecimalGdaOutcome[Decimal] 以及下一个 GdaContext
实数结果的经认证包络(IEEE 1788)ball_floatBallFloat, BallFloatDecorated区间,或在 BallContext 下的 (interval, BallFlags)
在首个错误处停止的二进制流水线bin_float_checkedBinFloatResult包装器内的 Result[BinFloat, ArithmeticError]
累积标志的 IEEE 十进制流水线decimal_checkedDecimalChecked有定义的值,以及最近一次与累积的 DecimalFlags
在陷阱处停止的 GDA 流水线decimal_gda_checkedGdaDecimalChecked串联传递的单一 GdaOutcome[Decimal]
在首个错误处停止的区间流水线ball_float_checkedBallFloatResult包装器内的 Result[BallFloat, ArithmeticError]
跨包比较值semanticSemanticScalar, SemanticInterval精确有理数,有意丢弃元数据

def 保存它们共享的少量基础词汇(Sign、PartialOrder、Floating trait 以及重新导出的 arithmetic 类型)。numeric_expr 和 frontend/* 用于解析器与符合性测试工具;internal/*、cli/*、consistency、doc_examples 和 bench/* 是仓库基础设施,不应作为应用的依赖。手册概览列出了所有包。

安装与导入

你需要 MoonBit 工具链 0.10 或更高版本(moonc ≥ 0.10)。添加本模块;如果你要直接使用 Luna-Flow/arithmetic 的舍入模式或上下文,也一并添加它:

moon add Luna-Flow/floating@0.8.0
moon add Luna-Flow/arithmetic

在 moon.pkg 中只导入你用到的包:

import {
  "Luna-Flow/arithmetic" @lf_arith,
  "Luna-Flow/floating/bin_float",
  "Luna-Flow/floating/decimal",
  "Luna-Flow/floating/ball_float",
}

导入的是包而不是文件:每个含有 moon.pkg 的目录就是一个包,其中的文件共享同一个命名空间。按照惯例,本手册将 Luna-Flow/arithmetic 导入为 @lf_arith,将 Luna-Flow/luna-generic 导入为 @lf_alg。

构造值

BinFloat 是带精度的精确二进数 c⋅2ec \cdot 2^e;Decimal 是 c⋅10qc \cdot 10^q,并记住其字面量的量子 qq;BallFloat 是端点为二进制值的区间。

///|
test "first values" {
  // 3 * 2^-1 at 53 bits of precision.
  let binary = @bin_float.BinFloat::make(
    @bin_float.BinCoeff::from_uint64(3UL),
    -1,
    53,
  )
  inspect(binary, content="3p-1")
  inspect(binary.to_shortest_string(), content="1.5")
  // Parsing keeps significant trailing zeros.
  let price = @decimal.Decimal::from_string("12.3400").unwrap()
  inspect(price, content="12.3400")
  inspect(price.quantum(), content="-4")
  // Every real number from 1 through 2.
  let interval = @ball_float.BallFloat::from_bounds(
    @bin_float.BinFloat::from_int(1),
    @bin_float.BinFloat::from_int(2),
  )
  inspect(interval.contains(binary), content="true")
}

BinFloat 以精确形式 <coefficient>p<exponent> 打印;如需十进制文本,请使用 to_shortest_string 或 to_decimal_string_ctx。仅当你想丢弃十进制数的同值类(cohort)时,才对其调用 normalized()。

其他构造函数:BinFloat::from_int、from_double、from_string 和 from_string_ctx(正确舍入的十进制解析)、from_hex;Decimal::from_int、from_string 和 from_string_ctx;BallFloat::from_int、from_double、exact 和 from_bounds。构造函数接受可选的 precision 参数;BallFloat::from_int 默认为 16 位,因此若需要类似 binary64 的端点,请传入 precision=53。

选择上下文

普通运算符(+、-、*、/ 以及 add、mul、…)以操作数的精度、就近舍入(偶数优先)和实际上无界的指数范围进行计算,并丢弃状态。当精度、指数范围、舍入方向、微小性(tininess)或状态标志是结果的一部分时,请使用带显式上下文的 *_ctx 形式:

///|
test "contextual arithmetic" {
  let ctx = @bin_float.BinaryContext::binary64()
  let (third, flags) = @bin_float.BinFloat::from_int(1).div_ctx(
    @bin_float.BinFloat::from_int(3),
    ctx,
  )
  inspect(third.to_shortest_string(), content="0.3333333333333333")
  inspect(flags.inexact(), content="true")
  // The same quotient rounded upward lands one ulp higher.
  let up = @bin_float.BinaryContext::binary64(rounding=RoundTowardPositive)
  let (high, _) = @bin_float.BinFloat::from_int(1).div_ctx(
    @bin_float.BinFloat::from_int(3),
    up,
  )
  inspect(high.sub(third) == third.ulp(), content="true")
  let decimal_ctx = @decimal.DecimalContext::decimal64()
  let (q, decimal_flags) = @decimal.Decimal::from_int(1).div_ctx(
    @decimal.Decimal::from_int(3),
    decimal_ctx,
  )
  inspect(q, content="0.3333333333333333")
  inspect(decimal_flags.contains(@decimal.Inexact), content="true")
}

上下文是不可变的值;库中没有任何地方读取全局舍入模式。IEEE 上下文返回单次运算的标志,由你自行 combine。GDA 上下文则随结果一起传递:每个 GdaOutcome 都返回下一个上下文,其状态会累积标志。

选择失败模型

本库有意提供多种失败通道。请选择与调用者必须观察到的内容相匹配的那一种:

  • 来自 Decimal::from_string 等简单构造函数的 Option,适用于错误输入不需要诊断信息的情形。
  • 来自 checked 运算(div_checked、sqrt、compare_checked、from_string、try_*_ctx 初等函数)的 Result[T, ArithmeticError]。
  • 与有定义的结果一同返回的 BinaryFlags、DecimalFlags 和 BallFlags。
  • GdaOutcome[T],即使触发陷阱也保留 GDA 定义的结果。
  • 流水线包装器:BinFloatResult 和 BallFloatResult 在首个错误处停止;DecimalChecked 保留有定义的 NaN 与无穷大结果并累积标志;GdaDecimalChecked 在陷阱处停止并保留其结果。
  • Empty、Entire 和 NaI,它们是区间值,而不是错误。
///|
test "pipelines" {
  let failed = @bin_float_checked.BinFloatResult::from_int(-4).sqrt()
  inspect(failed.is_err(), content="true")
  let total = @decimal_checked.DecimalChecked::parse(
      "1",
      @decimal.DecimalContext::decimal64(),
    )
    .div(@decimal.Decimal::from_int(3))
    .mul(@decimal.Decimal::from_int(3))
  inspect(total.value(), content="0.9999999999999999")
  // Accumulated over the pipeline versus raised by the last step.
  inspect(total.flags().contains(@decimal.Inexact), content="true")
  inspect(total.raised().contains(@decimal.Inexact), content="false")
}

不要把这些通道合并成一种异常类型或一个 Result:那样会抹去标准规定为可观察的语义。

正确解读结果

  • 在标量上,带符号零、无穷大、静默 NaN 与信号 NaN 以及 NaN 的 payload 都是可观察的。
  • compare、< 和排序使用一个全预序:其中所有 NaN 彼此相等且排在所有数之上,并且 −0=+0-0 = +0。如需 IEEE 语义,请使用 compare_checked、bin_float 的静默与信号谓词,或 total_order* 系列函数。
  • BinFloat 和 BallFloat 上的 == 比较的是表示(包括零的符号和精度);Decimal 上的 == 比较的是值。对二进制值做数值相等比较时,请使用 compare(a, b) == 0。
  • BallFloat 具有包含关系与集合关系(contains、subset、definitely_lt、…),而没有标量序。只要结果包络了精确结果,它就是正确的;紧致程度是另一项独立的质量指标。
  • SemanticScalar 跨包比较数学值,并丢弃精度、量子、带符号零、payload、装饰和标志。

继续阅读

  • 数值语义定义了舍入、ulp、标志、量子、带符号零、NaN 和包络,并给出其推导。
  • 架构解释了包的分层以及经认证的初等函数。
  • 验证列出了各项检查关卡以及每项符合性声明的确切范围。
  • 每个包都有教程、API 参考和设计页面;可以从 bin_float 教程、decimal 教程或 ball_float 教程开始。