Make abs and lohi exact; bring coverage to 100%#10
Merged
Merged
Conversation
`abs` of a `GVar` whose span crosses zero is a folded normal. Its first
three moments have a closed form, so compute them: the center is now
exact instead of being off by O(σ) and flagged through `moment_error`.
Beyond 8σ from zero the fold is a pure reflection to within roundoff,
and the moment differences lose their conditioning, so reflect there.
This requires `erf`, hence the new SpecialFunctions dependency.
`lohi` measures the radius from the rounded center, `max(center - lo,
hi - center)`, and widens by an ulp only when rounding breaks
containment. A narrow span far from zero was not contained before: an
ulp of σ is far smaller than an ulp of the center, so inflating
`(hi - lo)/2` could not recover it. A degenerate span now has σ == 0
rather than a subnormal radius, so `min(3 ± 1, 2.0)` is exactly 2.0.
Tests cover the whole public surface, including sums of `GVar`s, `abs`,
`min`/`max`, the integer-backed methods reachable only through
`GVar{Int}`, `show`, and the trait functions. The folded-normal moments
are checked against both the half-normal closed form and Monte Carlo.
Assisted-by: Claude Opus 4.8 <noreply@anthropic.com>
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.
Started as a coverage pass (66.1% → 100%), which turned up two correctness bugs along the way.
absis now exact when the span crosses zeroPreviously,
absalways reflected:abs(0 ± 1)returned0.0 ± 1.0. The center was wrong by O(σ) — the folded normal's mean isσ√(2/π) ≈ 0.8— and the code compensated by chargingσtomoment_errorrather than by giving the right answer.The folded normal's first three moments have a closed form, so
absnow computes them. Withα = c/σ,ϕthe standard normal density, ande = erf(α/√2) = E[sign(x)]:So
abs(0 ± 1)is now0.798 ± 0.603, exact, withmoment_error == 0;distruststill flags it, but through the skew (the half-normal's skewness is ≈0.995), which is the honest defect. Beyond|α| = 8the wrong-side mass is below roundoff andE[|x|²] - E[|x|]²starts to cancel badly, so the reflection branch is kept there. The two branches agree where they meet.This needs
erf, hence the new SpecialFunctions dependency.lohinow produces a span that actually contains[lo, hi]Two problems with
G((lo + hi)/2, nextfloat((hi - lo)/2)):nextfloatinflated even exact spans — a degenerate span got a subnormal radius, which is howmin(3 ± 1, 2.0)came out as2.0 ± 5.0e-324.loval(g) <= lo && hival(g) >= hi. When the span is narrow but far from zero, an ulp of σ is far smaller than an ulp of the center, so no amount of widening(hi - lo)/2can recover what rounding the center lost.Measuring the radius from the rounded center,
max(center - lo, hi - center), fixes both: it is exact in the narrow-span case (Sterbenz), needs at most a single ulp of widening in the rest (verified over 10⁶ adversarial spans — adjacent floats, extreme and mixed magnitudes), and leaves a degenerate span withσ == 0.Coverage
66.1% → 100%. New tests cover sums/differences of
GVars (which were entirely untested),abs,min/max, scalar arithmetic in both argument orders, the integer-backed methods (reachable only viaGVar{Int}, sinceGVar(1, 2)float-promotes),show,hash, and the trait functions. The folded-normal moments are checked against both the half-normal closed form and Monte Carlo; regression tests pin thelohicontainment property.175 tests pass on both release and LTS.
🤖 Generated with Claude Code