Skip to content

Keep NextDouble(min, max) below max and finite for wide bounds [patch] - #93

Merged
matt-edmondson merged 2 commits into
mainfrom
fix/next-double-range-bounds
Oct 6, 2026
Merged

matt-edmondson merged 2 commits into
mainfrom
fix/next-double-range-bounds

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #61

What was wrong

  • IRandomProvider.NextDouble(min, max) computed min + u * (max - min). When the unit draw was close to 1, rounding could land exactly on maxExclusive. For example, NextDouble(1.0, BitIncrement(1.0)) returned max about half the time.
  • Finite bounds of opposite sign overflowed the width, so NextDouble(double.MinValue, double.MaxValue) returned Infinity, or NaN when the unit draw was 0.
  • UniformDistributionProvider had the same width overflow, which broke Mean, Median, Cdf, Quantile, Pdf and Sample.

Change

  • In NextDouble(min, max), when the width is infinite it interpolates as min*(1-u) + max*u instead. If the result reaches max (and max > min), it returns the next double below max.
  • Added RandomHelpers.NextDown, because Math.BitDecrement is not available on netstandard2.1.
  • In UniformDistributionProvider:
    • Mean is now Min/2 + Max/2.
    • Pdf now uses a half width, which stays finite.
    • Cdf and Quantile switch to half-width and interpolation forms when the full width overflows.
    • Median follows from Quantile, and Sample follows from NextDouble.
    • Results for ordinary intervals are unchanged.

Tests

New RandomRangeBoundaryTests uses a constant-word stub provider:

  • At the top draw, NextDouble stays below max for (1, 1+ulp), (1, 2), (0.3, 0.7), (5, 5.5) and (0.9, 1).
  • NextDouble(MinValue, MaxValue) stays finite and below max for the draws 0, 2^63 and ulong.MaxValue.
  • Uniform(-1e308, 1e308) gives Mean == 0, Median == 0, Cdf(0) == 0.5, Quantile(0.5) == 0, a positive Pdf, and a finite Sample.

With the fix reverted all 9 cases fail; with it they pass. The full solution builds for every target framework, including netstandard2.1. The full suite passes: 935 tests, 0 failed.

This PR stands alone on main. It also test-merges cleanly with #92 (the related #60 fix), which touches a neighbouring line in RandomHelpers.cs.

🤖 Generated with Claude Code

https://claude.ai/code/session_01ABXREnMPNwAYQMdr4STcfE


Generated by Claude Code

Rounding the final addition could land exactly on the exclusive upper
bound when the unit draw was close to 1, and finite bounds of opposite
sign overflowed the width to Infinity, so NextDouble(MinValue, MaxValue)
returned Infinity or NaN. Interpolate between the bounds when the width
overflows and step down from max when the result reaches it.

UniformDistributionProvider had the same overflow in Mean, Median, Cdf,
Quantile, Pdf and Sample; they now use half-width forms that stay
finite.

Fixes #61

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ABXREnMPNwAYQMdr4STcfE
Comment thread Essentials/RandomHelpers.cs Fixed
@sonarqubecloud

sonarqubecloud Bot commented Oct 6, 2026

Copy link
Copy Markdown

@matt-edmondson
matt-edmondson merged commit 1829fb1 into main Oct 6, 2026
14 checks passed
@matt-edmondson
matt-edmondson deleted the fix/next-double-range-bounds branch October 6, 2026 22:42
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.

NextDouble(min, max) can return maxExclusive, and returns Infinity for wide finite bounds (Uniform distribution too)

2 participants