Skip to content

Keep the exponential CDF and quantile precise near zero - #58

Merged
matt-edmondson merged 3 commits into
mainfrom
fix/exponential-small-argument-precision
Sep 28, 2026
Merged

matt-edmondson merged 3 commits into
mainfrom
fix/exponential-small-argument-precision

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #52

What changed

ExponentialDistributionProvider.Cdf computed 1 - exp(-λx) and Quantile computed -log(1 - p) / λ. Both subtract from a number near one, so they keep only the digits that survive that rounding. The relative error is about ε/x. Below about 1e-16 the result was 0, and Quantile returned -0, which is outside the support.

  • I added SpecialFunctions.ExpM1 and SpecialFunctions.Log1P. Both use Kahan's correction, which divides by a quantity carrying the same rounding error so the error cancels, and are accurate to a few ulps. They are written out because double.ExpM1/LogP1 are unavailable on netstandard2.1 and are the naive subtraction on .NET.
  • Cdf is now -ExpM1(-λx) and Quantile is now -Log1P(-p) / λ. Quantile(0) now returns +0.
  • SpecialFunctions.cs is linked into the Exponential project, as it already is into Normal, LogNormal, Binomial and Poisson. I updated CLAUDE.md to match.

The triage on #52 says #51 (Geometric with tiny p) needs the same helpers. Once this lands, that fix can reuse them.

Tests

  • I added Exponential_Keeps_Its_Precision_Near_Zero. It covers the repro values from the issue at 1e-15 relative tolerance, checks that the quantile is never negative zero, and checks that ordinary and far-tail values are unchanged.
  • With the old provider restored, the new test fails (Cdf(1e-17) returned 0). With the fix it passes.
  • The full Essentials.Tests suite passes: 917/917. The Exponential project builds cleanly in Release for all six target frameworks.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VPis8BNX5ETFM2XytgJCz7


Generated by Claude Code

Cdf used 1 - exp(-rx) and Quantile -log(1 - p), which keep only the
digits of a number near one: below about 1e-16 both rounded to zero and
the quantile to negative zero. Add Kahan-corrected ExpM1 and Log1P to
SpecialFunctions and use them.

Fixes #52

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VPis8BNX5ETFM2XytgJCz7
Comment thread Shared/SpecialFunctions.cs Fixed
Comment thread Shared/SpecialFunctions.cs Fixed
Comment thread Shared/SpecialFunctions.cs Fixed
Comment thread Shared/SpecialFunctions.cs Fixed
Below epsilon both return the argument, which is exact to within an ulp
and keeps the Kahan correction off a 0/0; the underflow and pole checks
become ordered comparisons.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VPis8BNX5ETFM2XytgJCz7
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VPis8BNX5ETFM2XytgJCz7
@sonarqubecloud

Copy link
Copy Markdown

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.

Exponential Cdf/Quantile lose all precision near zero: Exponential(2).Cdf(1e-17) = 0 and Quantile(1e-17) = −0 (expected 2e-17 and 5e-18)

2 participants