Skip to content

h3go: check the projection against an exact evaluation - #155

Merged
justinhwang merged 1 commit into
masterfrom
h3go/exact-projection-test
Oct 2, 2026
Merged

justinhwang merged 1 commit into
masterfrom
h3go/exact-projection-test

Conversation

@justinhwang

Copy link
Copy Markdown
Collaborator

Summary

Adds a test that checks the pure-Go projection from a cell's face coordinates to a point on the unit sphere against the same formula evaluated in 256-bit arithmetic.

Every float test so far, including the conformance suite, compares pure Go with the C library. That shows agreement but cannot tell accuracy from shared error. This test can, because since toVec3 stopped calling atan2, atan, sin and cos the whole chain from integer IJK coordinates to a unit vector is rational arithmetic plus square roots, which math/big evaluates to any precision. The only transcendentals left are the sine and cosine of the twenty fixed face-axis azimuths, computed once by Taylor series.

What is checked

  • Centers. faceIJK.toVec3 for every cell at res 0 to 2 (full enumeration) and one descendant per base cell at every resolution to 15.
  • Boundaries. A walk that mirrors toCellBoundary and pentToCellBoundary step for step, so the inserted face-crossing vertices and the 2D line intersection that places them are covered along with the topological vertices. The walk is checked against Boundary() by vertex count on every cell.

Both compare the float64 unit vector with the exact one by chord length, which equals the angular separation at these magnitudes and avoids the asin and atan2 of the final conversion to degrees.

The oracle's constants are the definitions the float64 code approximates: the face center vectors, axis azimuths and gnomonic scale are the package's literals; sqrt(3)/2, 1/sqrt(7), 1/3 and the Class III rotation's sine and cosine (sqrt(3/28) and sqrt(25/28)) are computed exactly rather than taken from the rounded float64 constants.

Results

Scope Worst separation Time
Every cell to res 2, samples to res 15 (default) 3.9e-14 deg 1.0 s
Every cell to res 4 (H3GO_EXACT_PROJECTION_MAXRES=4) 3.9e-14 deg 15 s

One ulp of a unit-vector component is about 1.3e-14 degrees, so the worst case is about three ulps. The bound is 1e-13 degrees, roughly eight ulps and a hundred times tighter than the conformance suite's coordinate tolerance.

Combined with the conformance suite this gives the first bound on the reference itself: pure Go is within 1e-13 degrees of the exact formula, and C is within 1e-11 degrees of pure Go, so C is within about 1e-11 degrees of the exact formula.

Not covered

latLngToCell needs sine and cosine of arbitrary inputs and cellArea needs atan, so both stay under the agreement tests.

Test plan

  • go test -count=2 -race ./x/h3go/ passes
  • H3GO_EXACT_PROJECTION_MAXRES=4 passes
  • golangci-lint run ./x/h3go/ clean

🤖 Generated with Claude Code

Every float test so far compares pure Go with the C library, which shows
agreement but cannot tell accuracy from shared error. This test evaluates
the same chain from a cell's integer face coordinates to a unit vector in
256-bit arithmetic and measures how far the float64 result lands from it.

The chain is rational arithmetic plus square roots since toVec3 stopped
calling atan2, atan, sin and cos: the hex2d map uses sqrt(3), the
per-resolution scale is 1/sqrt(7), the Class III rotation's sine and
cosine are sqrt(3/28) and sqrt(25/28), the inverse gnomonic uses
1/sqrt(1+r^2), and the tangent basis is a normalization and a cross
product. The only transcendentals are the sine and cosine of the twenty
face-axis azimuths, which the test computes once by series. The face
center vectors, the azimuths and the gnomonic scale are taken as the
package's literals, which are the definition.

Centers are checked through faceIJK.toVec3. Boundaries are checked by a
walk that mirrors toCellBoundary and pentToCellBoundary, so the inserted
face-crossing vertices and the 2D line intersection that places them are
covered along with the topological vertices; the walk is checked against
Boundary() by vertex count. Comparison is by chord length between the two
unit vectors, which equals the angular separation at these magnitudes and
avoids the asin and atan2 of the final conversion to degrees.

Every cell at res 0 to 2 is enumerated by default, about one second, and
one descendant per base cell at every finer resolution. The variable
H3GO_EXACT_PROJECTION_MAXRES raises the enumerated resolution; res 4 takes
fifteen seconds. The largest separation observed is 3.9e-14 degrees of
arc, about three ulps of a unit-vector component; the bound is 1e-13.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@coveralls

Copy link
Copy Markdown

Coverage Report for CI Build 37064629401

Coverage remained the same at 100.0%

Details

  • Coverage remained the same as the base build.
  • Patch coverage: No coverable lines changed in this PR.
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 4538
Covered Lines: 4538
Line Coverage: 100.0%
Coverage Strength: 2695058.17 hits per line

💛 - Coveralls

@justinhwang
justinhwang merged commit 6f82ecd into master Oct 2, 2026
14 checks passed
@justinhwang
justinhwang deleted the h3go/exact-projection-test branch October 2, 2026 22:54
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.

2 participants