Skip to content

Deterministic ephemeris: vendor musl transcendentals, pin FP contraction - #29

Merged
heirloomlogic merged 1 commit into
mainfrom
heirloomlogic/issue-28-cross-platform
Jul 24, 2026
Merged

Deterministic ephemeris: vendor musl transcendentals, pin FP contraction#29
heirloomlogic merged 1 commit into
mainfrom
heirloomlogic/issue-28-cross-platform

Conversation

@heirloomlogic

Copy link
Copy Markdown
Owner

Closes #28.

Problem

Ephemeris positions were not bit-reproducible across environments: the vendored astronomy.c called the host libm's transcendentals (sin ×137, cos ×97, atan2 ×14, …), whose last-ULP results differ between macOS point releases (26.4 vs 26.5.2) and vs glibc. Positions drifted by sub-arcminute amounts per environment, silently flipping ~6 borderline cases in AstrologyKit's expert-locked calibration corpus and making hosted CI impossible to keep green.

Fix

Vendor the math, pin the codegen, lock it with bit-exact tests.

  • Sources/CLibAstronomy/detmath/ — musl 1.2.5 double-precision subset (sin cos tan asin acos atan atan2 exp log10 pow cbrt hypot + kernels: __rem_pio2, __rem_pio2_large, scalbn, exp/pow tables, error helpers). Every symbol is ak_-prefixed (including internals) so nothing can collide with or resolve to the host libm at link time. musl attribution preserved; MIT license added to THIRD_PARTY_NOTICES.
  • Local patch Fix lint warnings, Swiftlint and README updates #6 to astronomy.c (one include line, documented in the vendoring note): ak_detmath.h redirects the 12 transcendentals to ak_* and opens with #pragma STDC FP_CONTRACT OFF, pinning FMA contraction off for the whole translation unit. Build logs confirm Xcode defaults to -ffp-contract=on, so the pragma is load-bearing. Source-level on purpose — unsafeFlags would make the package unconsumable as a dependency.
  • pow.c #undefs __FP_FAST_FMA — musl's pow has an FMA path that is compiled on Apple Silicon but not x86; exactly the cross-arch divergence this PR eliminates.
  • Swift layer: the 3 transcendental call sites (MoonPhase.illumination's cos, FixedStar's atan2/asin) now call ak_* via the new public include/ak_math.h. sqrt/fmod/floor/ceil/fabs deliberately stay on the host — they are IEEE-exact/correctly rounded everywhere and carry no drift risk.
  • ReproducibilityTests — 158 bit-exact golden assertions (Double(bitPattern:), exact ==): ecliptic lon/lat/dist for 10 bodies at 4 instants (1980/2000/2026/2050), equatorial + topocentric coordinates, sunrise/sunset and full-moon search times, FixedStar and Moon.illumination Swift paths, Chiron. Identical bit patterns in debug and release. These running green on both the macOS and Ubuntu CI jobs is the cross-environment proof from the issue's acceptance criteria.
  • CI nm guard on the Linux job: fails if any built object regains an undefined reference to a host transcendental (so a future call site can't silently escape the redirect).
  • Docs: MAINTAINING.md gains patch Fix lint warnings, Swiftlint and README updates #6 in the re-apply checklist plus a "Deterministic math" section (musl update procedure, no-unsafeFlags constraint, Windows note: Swift-on-Windows compiles C via clang, which honors the STDC pragma).

Accuracy

Unchanged at the meaningful scale: all JPL Horizons validation suites (±1–1.5′) and audit validation suites pass untouched. The change is last-ULP only.

⚠️ Downstream impact (one-time)

Vendored math shifts last-ULP values relative to every current host libm, so AstrologyKit must re-baseline its calibration corpus once against this version. After that, the baseline holds permanently on every OS, runner image, and toolchain — which is the point.

Verification

  • swift test (debug): 579/579 in 175 suites ✅
  • swift test -c release: 579/579, same golden bit patterns ✅
  • swift test --sanitize=thread: 579/579 ✅
  • swift-format lint --strict (CI config): clean ✅
  • nm audit: zero undefined host-transcendental refs in astronomy.o and detmath objects; no fma
  • CI on this PR: Ubuntu (glibc, x86) and macOS (arm64) jobs asserting the same 158 bit patterns = the cross-OS/cross-arch reproducibility proof.

🤖 Generated with Claude Code

Positions were not bit-reproducible across environments because the
vendored astronomy.c called the host libm's transcendentals, whose
last-ULP results differ between macOS point releases and vs glibc.
Downstream calibration corpora baselined against these positions broke
whenever the runner OS rolled forward.

- Vendor a musl 1.2.5 double-precision math subset (sin, cos, tan,
  asin, acos, atan, atan2, exp, log10, pow, cbrt, hypot + internals)
  into Sources/CLibAstronomy/detmath/, all symbols ak_-prefixed so
  nothing can resolve to the host libm at link time.
- Redirect astronomy.c onto it via ak_detmath.h (local patch #6: one
  include line) and pin FMA contraction off for the whole translation
  unit with #pragma STDC FP_CONTRACT OFF (source-level; unsafeFlags
  would make the package unconsumable as a dependency). pow.c also
  #undefs __FP_FAST_FMA to kill an arm64-vs-x86 path divergence.
- Route the three Swift transcendental call sites (MoonPhase.cos,
  FixedStar.atan2/asin) through the new public ak_math.h. sqrt/fmod/
  floor/ceil/fabs stay on the host (IEEE-exact everywhere).
- Add ReproducibilityTests: 158 bit-exact golden assertions
  (Double.bitPattern) covering all bodies, topocentric coordinates,
  rise/set and moon-phase searches, FixedStar, and Chiron; identical
  in debug and release.
- Add a CI nm guard on the Linux job failing on any undefined host
  transcendental reference in the built objects.
- Document as local patch #6 in MAINTAINING.md with a deterministic-
  math section; musl MIT license in THIRD_PARTY_NOTICES; CHANGELOG
  flags the one-time last-ULP shift requiring downstream re-baseline.

Closes #28

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@heirloomlogic
heirloomlogic merged commit cda0443 into main Jul 24, 2026
9 checks passed
@heirloomlogic
heirloomlogic deleted the heirloomlogic/issue-28-cross-platform branch July 24, 2026 19:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Ephemeris positions are not reproducible across environments (OS libm dependency) — breaks downstream calibration

2 participants