floating_vs_decmial_x API
The package Luna-Flow/diff_bench/floating_vs_decmial_x compares
moonbitlang/x/decimal (X) with Luna-Flow/floating/decimal_gda@0.7.1 (GDA)
on identical decimal inputs, checks both against a BigInt oracle, and
measures them with Mare Mark. It is a benchmark harness kept in the GitHub
repository for reproduction; it is not a runtime dependency for other
projects. Functions abort on input outside their contract.
The package name keeps the historical spelling decmial. The
tutorial shows the items in use, and
the design page derives the precision
contracts. Source: src/floating_vs_decmial_x/.
Examples import the package as @floating_vs_decmial_x and
moonbitlang/core/bigint.
Neutral decimal model
These items have the same definitions as in
dzmingli_vs_floating; the
two packages keep separate copies so that each benchmark builds alone.
DecimalValue
DecimalValue is the implementation-neutral decimal with value
.
pub(all) struct DecimalValue {
coefficient : @bigint.BigInt
scale : Int
}
normalize
normalize removes trailing zeros from the coefficient while the scale stays
non-negative; zero becomes .
pub fn normalize(DecimalValue) -> DecimalValue
canonical_string
canonical_string renders the normalized value in positional notation
without an exponent; equal numbers give equal strings.
pub fn canonical_string(DecimalValue) -> String
parse_decimal_value
parse_decimal_value reads a finite decimal string, with optional sign,
point and exponent, into a normalized value. It aborts on anything else.
pub fn parse_decimal_value(String) -> DecimalValue
OracleResult
OracleResult holds a normalized value and its canonical string.
pub struct OracleResult {
value : DecimalValue
canonical : String
}
to_oracle_result
to_oracle_result normalizes a value and pairs it with its canonical string.
pub fn to_oracle_result(DecimalValue) -> OracleResult
test "neutral decimal model" {
let v = @floating_vs_decmial_x.parse_decimal_value("0.00120")
inspect(v.coefficient, content="12")
inspect(v.scale, content="4")
inspect(@floating_vs_decmial_x.canonical_string(v), content="0.0012")
let r = @floating_vs_decmial_x.to_oracle_result({ coefficient: -1200N, scale: 2 })
inspect(r.canonical, content="-12")
}
Exact arithmetic
add
add returns the exact sum after aligning both operands to the larger scale.
pub fn add(DecimalValue, DecimalValue) -> DecimalValue
subtract
subtract returns the exact difference after the same alignment.
pub fn subtract(DecimalValue, DecimalValue) -> DecimalValue
multiply
multiply returns the exact product.
pub fn multiply(DecimalValue, DecimalValue) -> DecimalValue
compare
compare returns -1, 0 or 1.
pub fn compare(DecimalValue, DecimalValue) -> Int
test "exact arithmetic" {
let a = @floating_vs_decmial_x.parse_decimal_value("123.45")
let b = @floating_vs_decmial_x.parse_decimal_value("-4.5")
let show = @floating_vs_decmial_x.canonical_string
inspect(show(@floating_vs_decmial_x.add(a, b)), content="118.95")
inspect(show(@floating_vs_decmial_x.subtract(a, b)), content="127.95")
inspect(show(@floating_vs_decmial_x.multiply(a, b)), content="-555.525")
inspect(@floating_vs_decmial_x.compare(b, a), content="-1")
}
Reference oracle
The oracle models X’s result policy: at most 28 fractional digits, extra digits truncated toward zero.
oracle_multiply
oracle_multiply returns the exact product, truncated toward zero to 28
fractional digits when its scale exceeds 28.
pub fn oracle_multiply(DecimalValue, DecimalValue) -> DecimalValue
oracle_divide
oracle_divide returns the quotient truncated toward zero to 28 fractional
digits, computed with integer division only.
pub fn oracle_divide(DecimalValue, DecimalValue) -> DecimalValue
With the result coefficient is
when and
otherwise, at scale 28. Aborts on a zero divisor. Repeating quotients are
fine here, unlike in dzmingli_vs_floating.
oracle_operation
oracle_operation evaluates an Operation with the rules above: exact
Add, Subtract and Compare, the truncating Multiply and Divide, and
the identity for Parse and Format.
pub fn oracle_operation(Operation, DecimalValue, DecimalValue) -> OracleResult
The oracle does not depend on DecimalSemantics; the
design page
shows why one oracle serves both groups.
test "reference oracle" {
let one = @floating_vs_decmial_x.parse_decimal_value("1")
let three = @floating_vs_decmial_x.parse_decimal_value("3")
inspect(
@floating_vs_decmial_x.oracle_operation(Divide, one, three).canonical,
content="0.3333333333333333333333333333",
)
let tiny : @floating_vs_decmial_x.DecimalValue = { coefficient: 1N, scale: 20 }
let wide : @floating_vs_decmial_x.DecimalValue = { coefficient: 123456789N, scale: 10 }
inspect(
@floating_vs_decmial_x.canonical_string(@floating_vs_decmial_x.oracle_multiply(tiny, wide)),
content="0.0000000000000000000001234567",
)
}
Operations, semantics and suites
Operation
Operation lists the operation families of this benchmark.
pub(all) enum Operation {
Add
Subtract
Multiply
Divide
Compare
Parse
Format
}
Parse and Format return the prepared left operand on both sides and are
not run by the executables.
operation_name
operation_name returns the snake-case name used in records, such as
"divide".
pub fn operation_name(Operation) -> String
DecimalSemantics
DecimalSemantics selects the semantic contract both implementations must
meet.
pub(all) enum DecimalSemantics {
ExactOverlap
XCompatible
}
ExactOverlap uses inputs whose exact result both libraries represent, so no
rounding happens on either side. XCompatible reproduces X’s
28-fractional-digit truncation in GDA by a quantize after multiply and
divide, inside the timed path.
decimal_semantics_name
decimal_semantics_name returns "exact_overlap" or "x_compatible".
pub fn decimal_semantics_name(DecimalSemantics) -> String
OperandShape
OperandShape labels a generated case; the label does not influence the
operands.
pub(all) enum OperandShape {
Small
Large
Boundary
Cancellation
Repeating
}
TimingScope
TimingScope names what a timed invocation contains, for suite metadata.
pub(all) enum TimingScope {
ConstructionAndOperation
OperationOnly
}
The Mare Mark runner does not read it. Comparison records carry a string scope
instead: "semantic_equivalent_pipeline" for Multiply and Divide under
XCompatible, "arithmetic_only" otherwise.
Suite
Suite is descriptive metadata for a benchmark suite.
pub struct Suite {
name : String
operations : Array[Operation]
scales : Array[Int]
timing_scope : TimingScope
warmup : Int
samples : Int
}
default_suite
default_suite returns the suite "decimal-x-floating-gda" with all seven
operations, scales [0, 2, 6, 18, 28], OperationOnly, 5 warmups and 20
samples.
pub fn default_suite() -> Suite
suite_case_count
suite_case_count returns operations × scales × cases per operation.
pub fn suite_case_count(Suite, Int) -> Int
test "operations, semantics and suites" {
inspect(@floating_vs_decmial_x.operation_name(Divide), content="divide")
inspect(@floating_vs_decmial_x.decimal_semantics_name(XCompatible), content="x_compatible")
let suite = @floating_vs_decmial_x.default_suite()
inspect(@floating_vs_decmial_x.suite_case_count(suite, 2), content="70")
}
Deterministic cases
generate_decimal
generate_decimal(seed, digits, scale, negative) builds a decimal string with
exactly digits coefficient digits in , scale of them fractional.
Aborts if digits <= 0 or scale < 0.
pub fn generate_decimal(Int, Int, Int, Bool) -> String
BenchmarkCase
BenchmarkCase is a generated case; its strings are the serialization
boundary.
pub struct BenchmarkCase {
id : String
operation : Operation
shape : OperandShape
left : String
right : String
scale : Int
}
generate_cases
generate_cases(seed, count, operation) returns count reproducible cases
with digits and scales , without clock or global random state.
pub fn generate_cases(Int, Int, Operation) -> Array[BenchmarkCase]
serialize_case
serialize_case joins decimal-neutral-v1 and the case fields with newlines.
pub fn serialize_case(BenchmarkCase) -> String
fingerprint_case
fingerprint_case hashes serialize_case with Mare Mark’s
stable_fingerprint.
pub fn fingerprint_case(BenchmarkCase) -> String
expand_digit_scales
expand_digit_scales(sizes, n) repeats each coefficient size n times, one
Mare Mark dataset per repetition.
pub fn expand_digit_scales(Array[Int], Int) -> Array[Int]
test "deterministic cases" {
inspect(@floating_vs_decmial_x.generate_decimal(0, 2, 4, true), content="-0.0018")
let first = @floating_vs_decmial_x.generate_cases(17, 4, Multiply)
let again = @floating_vs_decmial_x.generate_cases(17, 4, Multiply)
inspect(first[3].right == again[3].right, content="true")
assert_eq(@floating_vs_decmial_x.expand_digit_scales([1, 4], 3), [1, 1, 1, 4, 4, 4])
}
Fixtures and adapters
working_precision
working_precision(operation, left, right, semantics?) returns the GDA
context precision of a fixture. semantics defaults to XCompatible.
pub fn working_precision(Operation, DecimalValue, DecimalValue, semantics? : DecimalSemantics) -> Int
With the coefficient digit counts and the scales:
| Operation | Precision |
|---|---|
Add, Subtract | |
Multiply | |
Divide, ExactOverlap | |
Divide, XCompatible | |
Compare, Parse, Format |
The design page derives each line and the operand classes it covers.
DecimalFixture
DecimalFixture holds both implementations’ operands, built before timing.
pub struct DecimalFixture {
neutral_left : DecimalValue
neutral_right : DecimalValue
operation : Operation
semantics : DecimalSemantics
x_left : @decimal.Decimal
x_right : @decimal.Decimal
gda_left : @decimal_gda.Decimal
gda_right : @decimal_gda.Decimal
gda_quantum_28 : @decimal_gda.Decimal
gda_context : @decimal_gda.GdaContext
}
@decimal is moonbitlang/x/decimal. gda_quantum_28 is , the
quantum of the XCompatible post-processing.
prepare_fixture
prepare_fixture(operation, left, right, semantics?) computes the precision
, converts both operands for X and for GDA, and builds a GDA context with
precision and rounding toward zero.
pub fn prepare_fixture(Operation, DecimalValue, DecimalValue, semantics? : DecimalSemantics) -> DecimalFixture
semantics defaults to XCompatible. The GDA operands are parsed with
precision and half-even rounding, so an operand with more than
significant digits is rounded; see the
known limitation.
x_from_neutral
x_from_neutral converts a value with Decimal::new; it aborts when the
scale is outside X’s range .
pub fn x_from_neutral(DecimalValue) -> @decimal.Decimal
gda_from_neutral
gda_from_neutral(value, precision) parses the canonical string with
Decimal::from_string(precision~).
pub fn gda_from_neutral(DecimalValue, Int) -> @decimal_gda.Decimal
DecimalObservation
DecimalObservation is the raw result of one adapter call.
pub(all) enum DecimalObservation {
X(@decimal.Decimal)
XCompare(Int)
Gda(@decimal_gda.GdaOutcome[@decimal_gda.Decimal])
}
XCompare holds X’s comparison result already reduced to -1, 0 or 1.
run_x
run_x performs the fixture’s operation with X’s operators +, -, *,
/ or Compare::compare.
pub fn run_x(DecimalFixture) -> DecimalObservation
run_gda
run_gda performs the fixture’s operation with floating GDA; under
XCompatible it also quantizes products whose scale exceeds 28 and every
quotient to .
pub fn run_gda(DecimalFixture) -> DecimalObservation
canonical_x
canonical_x converts an X decimal to a normalized DecimalValue from its
coefficient and scale.
pub fn canonical_x(@decimal.Decimal) -> DecimalValue
canonical_observation
canonical_observation converts any observation to a normalized
DecimalValue; GDA results go through to_string and parse_decimal_value.
pub fn canonical_observation(DecimalObservation) -> DecimalValue
test "fixtures and adapters" {
let one = @floating_vs_decmial_x.parse_decimal_value("1")
let three = @floating_vs_decmial_x.parse_decimal_value("3")
inspect(@floating_vs_decmial_x.working_precision(Divide, one, three), content="31")
let fixture = @floating_vs_decmial_x.prepare_fixture(Divide, one, three)
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")
}
Mare Mark integration
MareDecimalInput
MareDecimalInput is the materialized input of one Mare Mark dataset.
pub(all) struct MareDecimalInput {
left : DecimalValue
right : DecimalValue
operation : Operation
semantics : DecimalSemantics
digits : Int
left_scale : Int
right_scale : Int
}
PerformanceResult
PerformanceResult summarizes one operation, semantic group and coefficient
size from paired confirmatory samples.
pub(all) struct PerformanceResult {
operation : Operation
semantics : DecimalSemantics
timing_scope : String
digits : Int
x_median_us : Double
gda_median_us : Double
gda_relative_delta_pct : Double
x_speedup_vs_gda : Double
decision : String
samples : Int
}
x_speedup_vs_gda is the GDA median divided by the X median (above means
X is faster). decision is "gda_faster", "x_faster", "equivalent",
"invalid" or "unknown". Unlike dzmingli_vs_floating, results are always
paired; a validation failure shows up only in the report counts.
PerformanceResult::to_json
PerformanceResult::to_json renders a "comparison" JSONL record with
artifact version mmka_1.
pub fn PerformanceResult::to_json(Self) -> String
MareBenchmarkReport
MareBenchmarkReport collects the results, raw JSONL and validation counts of
a run; validation_count counts passed and failed validations.
pub(all) struct MareBenchmarkReport {
results : Array[PerformanceResult]
jsonl : String
validation_count : Int
failed_count : Int
}
smoke_protocol
smoke_protocol returns the short test protocol: 2 warmups, 0.5 ms
calibration batches and 3 confirmatory repetitions.
pub fn smoke_protocol() -> @model.RunProtocol
default_benchmark_protocol
default_benchmark_protocol returns the protocol of the published runs: 5
warmups, 5 ms calibration batches, balanced blocks with seed 0xDEC1A1,
outliers reported only, and 20 confirmatory repetitions.
pub fn default_benchmark_protocol() -> @model.RunProtocol
benchmark_environment
benchmark_environment builds the environment snapshot from the MARE_*
environment variables, with fixed defaults for missing ones.
pub fn benchmark_environment() -> @model.EnvironmentSnapshot
run_mare_benchmark
run_mare_benchmark(operations, semantics, digit_scales, protocol, seed) runs
one Mare Mark experiment per operation under one semantic group and returns the
combined report.
pub async fn run_mare_benchmark(Array[Operation], DecimalSemantics, Array[Int], @model.RunProtocol, UInt64) -> MareBenchmarkReport
Each dataset is validated against the oracle before timing. The function does
not abort on failures; callers check failed_count. It runs on the native
and js targets.
async test "mare mark smoke run" {
let report = @floating_vs_decmial_x.run_mare_benchmark(
[Divide],
XCompatible,
[16],
@floating_vs_decmial_x.smoke_protocol(),
42UL,
)
inspect(report.failed_count, content="0")
inspect(report.results[0].timing_scope, content="semantic_equivalent_pipeline")
}
Reports
mare_performance_report_document
mare_performance_report_document(results, target, run_id) builds a Plot IR
document with one latency plot per operation and semantic group, and one
X-versus-GDA speedup plot per group.
pub fn mare_performance_report_document(Array[PerformanceResult], String, String, validation_count? : Int, failed_count? : Int) -> @ir_model.PlotDocument
The corpus summary is total validation_count + failed_count, passed
validation_count and failed failed_count. Because validation_count
already includes failures, the summary is only right when failed_count is
zero; see the design page.
mare_performance_report_html
mare_performance_report_html renders the same document as a self-contained
HTML page.
pub fn mare_performance_report_html(Array[PerformanceResult], String, String, validation_count? : Int, failed_count? : Int) -> String
test "performance report" {
let result : @floating_vs_decmial_x.PerformanceResult = {
operation: Add,
semantics: ExactOverlap,
timing_scope: "arithmetic_only",
digits: 16,
x_median_us: 1.0,
gda_median_us: 2.0,
gda_relative_delta_pct: 100.0,
x_speedup_vs_gda: 2.0,
decision: "x_faster",
samples: 60,
}
let html = @floating_vs_decmial_x.mare_performance_report_html(
[result],
"native",
"example",
validation_count=2,
)
inspect(html.contains("x_decimal"), content="true")
}