Review (#225): on Linux x86_64 a pure-Rust binary's f64::cbrt binds to compiler_builtins' copy (nm: local `t cbrt`), not glibc, and differs from glibc in 47.5% of cases (identical to wasm). Checked further: a binary that links -lm ahead of it, as solidcore-ffi's build.rs does, imports glibc's cbrt (`U cbrt`), and its Rust and C++ sides agree on all 200k inputs. So difftest's geom.polynomial parity is real, but does not carry over to pure-Rust builds, whose cubic solver can differ from the C++ by an ulp. All other functions match glibc exactly in a pure-Rust Linux binary (all 13 checked). - findings: reworded opening, "Rust Linux x86_64" column, link-order note, consequence for geom.polynomial. - mathprobe: README wording; run.sh also evaluates the host's C libm and compare.py prints Rust-vs-host-libm, so such a function shows up on any host. - rustfmt the probe sources; drop trailing whitespace in compare.py. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
mathprobe: does f64::sin give the same bits everywhere?
difftest proves solidcore matches the C++ bit for bit on Linux x86_64 with glibc.
There, Rust's f64::sin and the C++ sin both call glibc's sin, and so does every
other measured function except cbrt. compiler_builtins defines its own cbrt, and
a pure-Rust Linux binary binds to it unless libm is linked earlier. On other targets,
Rust uses a different libm: Apple's on macOS, and on wasm32-unknown-unknown the
pure-Rust libm in compiler_builtins. This probe measures how often those differ.
For each std f64 method solidcore calls (sin, cos, tan, asin, acos, atan,
atan2, sinh, cosh, ln, log10, cbrt, powf, powi; sqrt is correctly
rounded everywhere, so it is left out), gen.py writes 200,000 deterministic inputs.
They are weighted toward what a geometry kernel passes: angles within a few turns,
values near multiples of π/2, near-±1 arguments for acos/asin, and wide log-uniform
ranges. The probe then evaluates them
- on the host, as a Rust binary (
src/bin/native.rs); - as a
wasm32-unknown-unknownmodule run by node (wasmeval.mjs); node passesf64values through bit for bit, and the math runs inside the module; - with docker, through Python's
mathmodule (a thin wrapper over the C libm) inubuntu:24.04onlinux/amd64andlinux/arm64. That is glibc 2.39, the same base image as CI's hosted runners;
and compare.py reports, per function, how many results differ from glibc x86_64
in any bit, and by how many ulp at most. It also compares the host's Rust binary with
Python on the same host (the host's C libm). A nonzero row there means Rust does not
call the platform libm for that function, like cbrt in a pure-Rust Linux binary.
harness/mathprobe/run.sh # work files in bin/mathprobe/
It needs Python 3.11+, node, a Rust toolchain with the wasm32-unknown-unknown target
(set CARGO=/RUSTC= if the cargo on PATH is not rustup's) and, for the glibc
columns, docker. Under emulation linux/amd64 must report fma (printed before
its row), or glibc takes different code paths than on CI's hardware.
The results, and what they mean for difftest and wasm, are in plan/findings.md
(General, "Math libraries differ by platform").