Summary
Activity route-derived distance differs by one binary64 ULP between the public Swift and Kotlin twins for a reachable ordered three-point route. Swift and an independent args-derived oracle produce 0x403e89813f76c75c; Kotlin produces 0x403e89813f76c75d.
Verified on meta032 032983d2bed9bf50dfcfc4d06ef707f3818cdc26 and current upstream main aa0037acbf7fc2ef972ba4676ae3e18d508be1ef. The relevant sources are unchanged between those revisions.
Exact reproduction
Input route, in order:
| point |
latitude |
longitude |
| 1 |
52.5 |
13.4 |
| 2 |
52.5001 |
13.4001 |
| 3 |
52.5002 |
13.4003 |
Exact binary64 results:
| calculation |
decimal metres |
bits |
| segment 1, both |
13.01783294966102 |
402a0921667f2bac |
| segment 2, Swift/oracle |
17.519295466875995 |
403184f08c373186 |
| segment 2, Kotlin |
17.519295466876 |
403184f08c373187 |
| total, Swift/oracle |
30.537128416537016 |
403e89813f76c75c |
| total, Kotlin |
30.53712841653702 |
403e89813f76c75d |
Final delta: 3.552713678800501e-15 metres, exactly one ULP at this result.
Cause
Earth radius, haversine expression, segment order, and route accumulation order match.
Swift converts degrees as x * pi / 180. Kotlin calls Math.toRadians, equivalent for this purpose to x / 180 * pi. The different intermediate rounding changes the second segment by one ULP and therefore changes the final total by one ULP.
Reachability and impact
GPX, TCX, and FIT imports use routeDistanceM when the file supplies a usable route but no authoritative summary/lap/session distance. Both app import paths then persist activity.distanceM as a database Double. The value is also available to workout detail, aggregate distance, local access, CSV/route export, and sharing paths.
Impact is negligible for a person: current UI formatting rounds far above this delta, and activity-file imports deliberately persist strain as nil/null. No distance-to-strain or distance-to-scoring dependency was found. This is P2 because it violates the deterministic exact Swift/Kotlin twin contract and blocks exact parity validation, not because it materially changes a displayed workout metric.
Existing coverage
Public importer tests cover route fallback only with broad tolerances: approximately 222 metres with 30-metre accuracy on both platforms. Kotlin RouteMath tests use 20-metre and 1-metre tolerances. Those tests cannot detect a one-ULP cross-platform drift.
The exact parity case activity_distance_ordered_three_points exposed the mismatch. Existing Native evidence stopped at this new gate; it was not retried or fixed.
Provenance
Kotlin RouteMath was introduced with the Android GPS workout work on 2026-06-10 (commits 27d7c79ca5deae736e7ae168ea1bde736e532221 / 79ca428d393d529dffd01ff09ae7e184047ab8a6). The paired Swift and Kotlin activity-file importers were introduced together on 2026-06-20 in commit 4b69da6fa0b950687d4268ce525a5033b3cb9835. The differing conversion forms have therefore been inherited since the Swift twin was added.
Duplicate audit
Open and closed issues and pull requests in bhelm/noop and ryanbr/noop were searched for activity_distance_ordered_three_points, routeDistanceM, haversine, toRadians, 1 ULP, route distance parity, the exact result bits, and the exact coordinates. No exact duplicate exists.
Related but distinct:
Upstream-only acceptance
- Choose and document one explicit degree-to-radian operation order as canonical for both platforms.
- Preserve the current earth radius, haversine formula, point order, and accumulation order.
- Add mirrored public tests that compare exact
Double bit patterns for the three-point route above and an adjacent one-segment control.
- The three-point result must be identical on Swift and Kotlin. If the existing Swift/oracle order is selected, the expected bits are
403e89813f76c75c.
- Preserve existing empty/single-point zero behavior and ordinary route-distance accuracy.
- Implement Production only upstream from a fresh current-main branch. Do not transplant a Product fix into the fork parity harness.
- Native execution requires separate authorization.
Summary
Activity route-derived distance differs by one binary64 ULP between the public Swift and Kotlin twins for a reachable ordered three-point route. Swift and an independent args-derived oracle produce
0x403e89813f76c75c; Kotlin produces0x403e89813f76c75d.Verified on meta032
032983d2bed9bf50dfcfc4d06ef707f3818cdc26and current upstream mainaa0037acbf7fc2ef972ba4676ae3e18d508be1ef. The relevant sources are unchanged between those revisions.Exact reproduction
Input route, in order:
Exact binary64 results:
402a0921667f2bac403184f08c373186403184f08c373187403e89813f76c75c403e89813f76c75dFinal delta:
3.552713678800501e-15metres, exactly one ULP at this result.Cause
Earth radius, haversine expression, segment order, and route accumulation order match.
Swift converts degrees as
x * pi / 180. Kotlin callsMath.toRadians, equivalent for this purpose tox / 180 * pi. The different intermediate rounding changes the second segment by one ULP and therefore changes the final total by one ULP.Reachability and impact
GPX, TCX, and FIT imports use
routeDistanceMwhen the file supplies a usable route but no authoritative summary/lap/session distance. Both app import paths then persistactivity.distanceMas a databaseDouble. The value is also available to workout detail, aggregate distance, local access, CSV/route export, and sharing paths.Impact is negligible for a person: current UI formatting rounds far above this delta, and activity-file imports deliberately persist
strainasnil/null. No distance-to-strain or distance-to-scoring dependency was found. This is P2 because it violates the deterministic exact Swift/Kotlin twin contract and blocks exact parity validation, not because it materially changes a displayed workout metric.Existing coverage
Public importer tests cover route fallback only with broad tolerances: approximately 222 metres with 30-metre accuracy on both platforms. Kotlin
RouteMathtests use 20-metre and 1-metre tolerances. Those tests cannot detect a one-ULP cross-platform drift.The exact parity case
activity_distance_ordered_three_pointsexposed the mismatch. Existing Native evidence stopped at this new gate; it was not retried or fixed.Provenance
Kotlin
RouteMathwas introduced with the Android GPS workout work on 2026-06-10 (commits27d7c79ca5deae736e7ae168ea1bde736e532221/79ca428d393d529dffd01ff09ae7e184047ab8a6). The paired Swift and Kotlin activity-file importers were introduced together on 2026-06-20 in commit4b69da6fa0b950687d4268ce525a5033b3cb9835. The differing conversion forms have therefore been inherited since the Swift twin was added.Duplicate audit
Open and closed issues and pull requests in
bhelm/noopandryanbr/noopwere searched foractivity_distance_ordered_three_points,routeDistanceM,haversine,toRadians,1 ULP,route distance parity, the exact result bits, and the exact coordinates. No exact duplicate exists.Related but distinct:
Upstream-only acceptance
Doublebit patterns for the three-point route above and an adjacent one-segment control.403e89813f76c75c.