floating_vs_decmial_x 教程
本教程说明如何在两个语义组下分别比较 moonbitlang/x/decimal(X)与 floating 的 decimal_gda(GDA),对照预言机检查它们,运行一次小型 Mare Mark 测量,以及复现已发布的基准。为什么这样选择分组和精度见设计页。
快速开始
本仓库没有发布到 mooncakes;请克隆它,并在模块内或包含它的 moon.work 工作区中工作:
git clone https://github.com/Luna-Flow/diff_bench.git
cd diff_bench
moon test --target native
在模块内某个包的 moon.pkg 中导入该包:
import {
"Luna-Flow/diff_bench/floating_vs_decmial_x",
}
最小的有用程序在两个库中按 X 的策略计算 除以 :
test "quick start" {
let one = @floating_vs_decmial_x.parse_decimal_value("1")
let three = @floating_vs_decmial_x.parse_decimal_value("3")
let fixture = @floating_vs_decmial_x.prepare_fixture(Divide, one, three, semantics=XCompatible)
let show = (o : @floating_vs_decmial_x.DecimalObservation) => {
@floating_vs_decmial_x.canonical_string(@floating_vs_decmial_x.canonical_observation(o))
}
inspect(show(@floating_vs_decmial_x.run_x(fixture)), content="0.3333333333333333333333333333")
inspect(show(@floating_vs_decmial_x.run_gda(fixture)), content="0.3333333333333333333333333333")
}
两者都返回截断到 28 位小数的商;GDA 通过一次 divide 加一次 quantize 得到它。
日常任务
选择语义组
ExactOverlap 适用于两个库都能表示其精确结果的输入。此时 GDA 只运行一次运算,并且必须返回精确值:
test "exact overlap" {
let a = @floating_vs_decmial_x.parse_decimal_value("12345.6789")
let b = @floating_vs_decmial_x.parse_decimal_value("8")
let fixture = @floating_vs_decmial_x.prepare_fixture(Divide, a, b, semantics=ExactOverlap)
let gda = @floating_vs_decmial_x.canonical_observation(@floating_vs_decmial_x.run_gda(fixture))
inspect(@floating_vs_decmial_x.canonical_string(gda), content="1543.2098625")
inspect(@floating_vs_decmial_x.working_precision(Divide, a, b, semantics=ExactOverlap), content="12")
}
XCompatible 重现 X 的 28 位截断。一个有 36 位小数的积在两边都被截到 28 位:
test "x-compatible product" {
let a : @floating_vs_decmial_x.DecimalValue = { coefficient: 123456789N, scale: 18 }
let b : @floating_vs_decmial_x.DecimalValue = { coefficient: 987654321N, scale: 18 }
let fixture = @floating_vs_decmial_x.prepare_fixture(Multiply, a, b, semantics=XCompatible)
let show = (o : @floating_vs_decmial_x.DecimalObservation) => {
@floating_vs_decmial_x.canonical_string(@floating_vs_decmial_x.canonical_observation(o))
}
inspect(show(@floating_vs_decmial_x.run_x(fixture)), content="0.0000000000000000001219326311")
inspect(show(@floating_vs_decmial_x.run_gda(fixture)), content="0.0000000000000000001219326311")
}
精确积为 ;截断保留到 为止的各位。
对照预言机检查语料
oracle_operation 实现了 X 的策略,因此它是两个组的参考:
test "corpus against the oracle" {
let ops : Array[@floating_vs_decmial_x.Operation] = [Add, Subtract, Multiply, Divide, Compare]
let mut checked = 0
for op in ops {
for case in @floating_vs_decmial_x.generate_cases(73, 20, op) {
let left = @floating_vs_decmial_x.parse_decimal_value(case.left)
let right = @floating_vs_decmial_x.parse_decimal_value(case.right)
let expected = @floating_vs_decmial_x.oracle_operation(op, left, right).canonical
let fixture = @floating_vs_decmial_x.prepare_fixture(op, left, right)
for observation in [
@floating_vs_decmial_x.run_x(fixture),
@floating_vs_decmial_x.run_gda(fixture),
] {
let got = @floating_vs_decmial_x.canonical_observation(observation)
assert_eq(@floating_vs_decmial_x.canonical_string(got), expected)
}
checked += 1
}
}
inspect(checked, content="100")
}
生成的用例至多 24 位,低于每个除法精度,因此操作数舍入限制不适用于它们。
运行一次小型 Mare Mark 测量
async test "smoke measurement" {
let report = @floating_vs_decmial_x.run_mare_benchmark(
[Add, Multiply],
ExactOverlap,
[4, 16],
@floating_vs_decmial_x.smoke_protocol(),
42UL,
)
inspect(report.failed_count, content="0")
inspect(report.validation_count, content="8")
inspect(report.results[0].timing_scope, content="arithmetic_only")
}
在 native 或 js 上运行它;该函数是 async 的。每个结果行包含以微秒计的 X 与 GDA 中位数以及 x_speedup_vs_gda,即 GDA 中位数除以 X 中位数。
复现已发布的基准
在仓库根目录执行:
moon run --release src/floating_vs_decmial_x/bench --target native \
> artifacts/floating_vs_decmial_x/scaling.jsonl
moon run --release src/floating_vs_decmial_x/bench_common --target native \
> artifacts/floating_vs_decmial_x/common_digits.jsonl
第一条命令测量 1 到 4,096 位,第二条测量 1、4、8、16、18 和 28 位。两者都在 JSONL 旁边写出一份 HTML 报告(见 bench 页)。设置 MARE_CPU、MARE_OS、MARE_BUILD_MODE 及其他 MARE_* 变量以记录主机;未设置的事实写作 unknown 或 unspecified。然后渲染图:
python3 tools/layout_x_decimal.py
按计时范围阅读记录:arithmetic_only 是一次公开运算,semantic_equivalent_pipeline 是在 GDA 中重现 X 策略的完整序列。来自不同目标的结果从不合并。
进阶
你自己输入的精度。 对 XCompatible 除法,working_precision 取决于操作数的位数差,而不是它们的长度。长于该精度的输入会在构造夹具时被舍入(见设计页)。需要保证一致时,让两个操作数都至多 位。
解读加速比。 x_speedup_vs_gda 是配对样本的中位数之比;decision 对配对差的中位数应用 3 % 的实际阈值。两者都不是置信区间。兄弟基准的统计一节给出了公式。
兄弟包。 dzmingli_vs_floating 以精确预言机、带解析的第二个计时范围和 19 种运算应用相同的方法。
常见陷阱
prepare_fixture和working_precision默认使用XCompatible。需要精确组时请显式传入semantics=ExactOverlap。- 在
XCompatible除法中,有效位数多于精度的操作数会在 GDA 做除法之前被舍入:GDA 于是可能在 X 返回 时返回 ,计时比较的也是较短的 GDA 操作数和完整的 X 操作数。 x_from_neutral在标度大于 28 时中止;X 无法保存这样的值。- HTML 汇总行只在没有失败的运行中才正确;请改为从报告中读取
validation_count和failed_count。 - 在当前的
moonbitlang/core中,BigInt::from_string在wasm-gc上对长字符串给出错误结果。测试division precision follows the requested semantic contract用它构造一个由 4,096 个 9 组成的操作数,并期望4097——这是那次错误解析产生的值;正确的工作精度是 。因此该测试在wasm-gc上通过,在native和js上失败。请改用parse_decimal_value或BigInt算术构造长测试值。 - X 的已发布数字是用
moonbitlang/x@0.4.46测得的;模块现在依赖0.5.5,而 JSONL 的实现记录仍写着0.4.46。