chain: fix IEEE-754 float noise in tool JSON output (#1) - #2
Merged
kiki830621 merged 1 commit intoMay 29, 2026
Merged
Conversation
Tool output rendered non-exact Doubles via JSONSerialization's 17-digit formatter, so coordinates like 25.04 leaked as 25.039999999999999. Rounding the value cannot fix this — 25.04 has no exact IEEE-754 form, so the rounded result is the same bit pattern; the noise lives in the formatter, not the value. Route every serialized object through JSONSanitize.clean, which recursively rewrites each Double to NSDecimalNumber(Double.description) — the shortest string that round-trips bit-exactly. Int/Bool type tags are preserved and inf/nan pass through unchanged (JSONSerialization rejects them as before). Covers all 8 serialization sites (6 jsonResult helpers + 2 inline RailTools), so coordinates inside any payload (incl. rail station matches) are cleaned, not just the bus-position site named in the issue. Refs #1
kiki830621
force-pushed
the
idd/chain-1-round-lat-lon-in-executor-json-output-to
branch
from
May 29, 2026 11:57
0f90695 to
1ea4430
Compare
kiki830621
deleted the
idd/chain-1-round-lat-lon-in-executor-json-output-to
branch
May 29, 2026 12:32
This was referenced May 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Single-root IDD chain for #1.
Root cause (corrected from the issue's premise)
The issue proposed rounding lat/lon to ~6 decimals. That is a no-op:
25.04has no exact IEEE-754Doublerepresentation, so(d * 1e6).rounded() / 1e6lands on the same bit pattern andJSONSerializationstill emits25.039999999999999. The noise lives in the 17-significant-digitDoubleformatter, not the value — and it affects every non-exactDoublethis codebase re-serializes (coordinates, speeds, fares), not just lat/lon.Fix
New
JSONSanitize.clean(_:)recursively rewrites everyDoubletoNSDecimalNumber(string: d.description)— the shortest string that round-trips — wired into all 8 sites that re-serialize codebase-constructed dicts (6jsonResulthelpers + 2 inlineRailToolssites). Centralizing below the call sites also cleans coordinates riding inside payloads the issue didn't name (e.g.rail_search_stationsmatches).Guarantees (empirically verified across the full Double domain):
-0.0(renders0;JSONSerializationalready drops the sign on round-trip regardless).NSNumber/Floatin the dicts (all reals areDouble); native scalars don't matchas Double(confirmed viaNSNumber.objCType).inf/nanrejected byJSONSerializationexactly as before.NSDecimalNumber's ±128 exponent ceiling fall back to raw formatting = no regression (never occur in transport data).25not25.0(identical JSON number, round-trips to25.0).Scope — raw TDX passthroughs correctly exempt
Four sites forward TDX's original response bytes verbatim (no deserialize→Double→reserialize round-trip), so they introduce none of our IEEE-754 noise and are intentionally NOT routed through the sanitizer (routing them would require parsing TDX JSON, re-introducing the very problem):
RailTools.swift:199rail_find_trainsRailTools.swift:223rail_status_trainRailTools.swift:251rail_status_stationMaritimeTools.swift:103-106maritime_status_scheduleVerification
testBusStatusPositionsAssemblesPositionsRED→GREEN (asserts clean"lat":25.04).JSONSanitizeTests: numerically-exact round-trip across 13 adversarial values (incl.1e-7,1e300,Double.greatestFiniteMagnitude,-0.0), Int/Bool preservation, recursion, non-finite, strings/null.nearby.Refs #1
Chain stops at verified — run
/idd-close #1to close after review.