快速上手
本指南带你从一个空的 MoonBit 项目开始,用 Luna-Flow/floating 得到正确的初步结果:选择包、安装、构造值、选择精度与失败的报告方式,以及解读结果。每个示例都能在当前分支上编译。
选择数值域
根据程序所需的语义来选择包,而不是根据输入的书写形式。
| 需求 | 包 | 主要类型 | 结果 |
|---|---|---|---|
| 任意精度二进制值,IEEE 754 二进制格式 | bin_float | BinFloat | 值,或在 BinaryContext 下的 (value, BinaryFlags) |
| IEEE 754 十进制算术与 decimal32/64/128 交换格式 | decimal | Decimal | 值,或在 DecimalContext 下的 (value, DecimalFlags) |
| 带粘滞状态与陷阱的 General Decimal Arithmetic | decimal_gda | Decimal | GdaOutcome[Decimal] 以及下一个 GdaContext |
| 实数结果的经认证包络(IEEE 1788) | ball_float | BallFloat, BallFloatDecorated | 区间,或在 BallContext 下的 (interval, BallFlags) |
| 在首个错误处停止的二进制流水线 | bin_float_checked | BinFloatResult | 包装器内的 Result[BinFloat, ArithmeticError] |
| 累积标志的 IEEE 十进制流水线 | decimal_checked | DecimalChecked | 有定义的值,以及最近一次与累积的 DecimalFlags |
| 在陷阱处停止的 GDA 流水线 | decimal_gda_checked | GdaDecimalChecked | 串联传递的单一 GdaOutcome[Decimal] |
| 在首个错误处停止的区间流水线 | ball_float_checked | BallFloatResult | 包装器内的 Result[BallFloat, ArithmeticError] |
| 跨包比较值 | semantic | SemanticScalar, 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 是带精度的精确二进数 ;Decimal 是 ,并记住其字面量的量子 ;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 彼此相等且排在所有数之上,并且 。如需 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教程开始。