internal 教程
本页面向数值核心的维护者,介绍 internal 中的辅助函数如何组合使用:将十进制系数舍入到指定精度、将十进制字面量解析为精确的组成部分,以及编写一个对舍入结果进行认证的细化循环。该包是 Luna-Flow/floating 的内部包;这些示例是为模块内部代码编写的,不会针对已发布的包进行编译。
快速入门
在模块内部导入该包(字面量还需导入 bigint):
import {
"Luna-Flow/floating/internal",
"moonbitlang/core/bigint",
}
以两种模式将 舍入为整数:
///|
test "rounded quotient" {
let mode_even = @lf_arith.RoundingMode::ToNearestEven
let mode_down = @lf_arith.RoundingMode::TowardZero
inspect(@internal.round_positive_div(2N, 3N, false, mode_even), content="1")
inspect(@internal.round_positive_div(2N, 3N, false, mode_down), content="0")
}
round_positive_div 分别接收量值 和真实商的符号,因为对于负数,定向舍入模式对量值的舍入方向不同。
日常任务
将十进制系数舍入到 p 位数字
位数多于 的十进制数 的舍入方式是:将系数除以 (其中 ),并把 加到指数上:
///|
fn round_coefficient(
coefficient : BigInt,
exponent : Int,
precision : Int,
mode : @lf_arith.RoundingMode,
) -> (BigInt, Int) {
let negative = coefficient < 0N
let magnitude = @internal.abs_bigint(coefficient)
let excess = @internal.digits10(magnitude) - precision
if excess <= 0 {
return (coefficient, exponent)
}
let rounded = @internal.round_positive_div(
magnitude,
@internal.pow10(excess),
negative,
mode,
)
(if negative { -rounded } else { rounded }, exponent + excess)
}
///|
test "round to three digits" {
let (c, q) = round_coefficient(-123456N, -3, 3, @lf_arith.RoundingMode::TowardNegative)
inspect(c, content="-124")
inspect(q, content="0")
}
进位可能产生 (例如 );实际的核心会对这种情况重新规格化,此示意代码省略了这一步。
将字面量解析为精确的组成部分
split_decimal_string 为十进制解析器完成词法层面的工作;对结果去除尾随零即可得到规范系数:
///|
test "parse then trim" {
guard @internal.split_decimal_string("-0.012500e2") is Some((negative, digits, exponent)) else {
fail("expected a literal")
}
let coefficient = BigInt::from_string(digits)
let (c, q, dropped) = @internal.trim_trailing_decimal_zeros(coefficient, exponent)
inspect(negative, content="true")
inspect(c, content="125")
inspect(q, content="-2")
inspect(dropped, content="2")
}
因此 。
用细化循环认证舍入
经认证的初等函数遵循 Ziv 策略:在某个工作精度下计算一个包络,只有当包络两端舍入到同一个数时才接受舍入结果;否则提高精度。预算限定了循环的次数。这里的“函数”是精确比值 ,向下舍入到 target 个小数位(二进制):
///|
fn floor_ratio_certified(
n : BigInt,
d : BigInt,
target : Int,
) -> Result[@internal.CertifiedDyadic, @lf_arith.ArithmeticError] {
let mut budget = @internal.CertifiedRefinementBudget::new(target + 2)
while budget.available() {
let enclosure = match
@internal.certified_dyadic_fraction(n, d, budget.precision()) {
Ok(value) => value
Err(error) => return Err(error)
}
let low = enclosure.lower().round_down(target)
let high = enclosure.upper().round_down(target)
if low.compare(high) == 0 {
return Ok(low)
}
budget = budget.next()
}
Err(
@internal.certified_failure(
"floor_ratio",
@lf_arith.CertificationStage::TargetRounding,
@lf_arith.CertificationFailureReason::RefinementBudgetExhausted,
target,
budget,
),
)
}
///|
test "certified floor of 1/3" {
let third = floor_ratio_certified(1N, 3N, 8).unwrap()
inspect(third.numerator(), content="85")
inspect(third.scale(), content="8")
}
深入了解
bin_float、decimal、decimal_gda和ball_float以目标精度加上保护余量来创建细化预算,并在预算耗尽时报告certified_failure;经认证的初等函数参见 bin_float 设计。semantic使用ExactRat作为有限二进制数和十进制数的规范精确值。consistency包以BigInt预言为参照测试这些辅助函数。- 在包含该模块的工作区中运行该包的测试:
moon test -p Luna-Flow/floating/internal。
常见陷阱
- 带符号的输入。
round_positive_div遇到负的分子会中止;请分别传入量值和符号。 - 时的
round_shift原样返回输入;它从不左移。 - 零会丢失其指数。 对于零系数,
remove_factor2、remove_factor10和trim_trailing_decimal_zeros返回指数 0。如果零的同值类(cohort)很重要,请自行保存原始指数。 - 未规格化的二进有理数。 不同尺度的
CertifiedDyadic值可能在数值上相等;请用compare比较,而不是逐字段比较。
后续步骤
- internal API
- internal 设计
- consistency 教程:交叉检验这些辅助函数的测试。