Hi, The code in Commons RNG provides a general interface for generating primitive values in UniformRandomProvider [1]. This closely matches the JDK's own interface in RandomGenerator (Java 17+) [2]. Although it is possible to add more methods to UniformRandomProvider this risks cluttering the interface with specialist methods that may not be commonly used. If it's not in the JDK's interface then typically we would not support it.
The interfaces are mostly the same. Differences are: UniformRandomProvider: void nextBytes(byte[] bytes, int start, int len) RandomGenerator: double nextExponential() double nextGaussian() If you wish to sample from a exponential or Gaussian then we have samplers in the sampling module. These include the same sampling method used in the JDK which is based on McFarland's modification of a ziggurat algorithm by Marsaglia. If you wish to sample from an open interval then we have the ContinuousUniformSampler [3] that samples within [lo, hi) by default but can be changed to an open interval of (lo, hi) with a constructor argument. Since the range can use any double values this requires some floating-point computations to map a generated [0, 1) to the interval [lo, hi), or (lo, hi). Since rounding can occur you can see values at the bounds even when the original double was non-zero. So a rejection algorithm is used: that is if sample == lo or sample == hi then repeat. Rejection frequency is small unless the range between lo and hi does not contain many floating-point values. Thus this rejection is efficiently ignored due to branch prediction. Note that the constructor for this sampler validates there are values between lo and hi. Otherwise you can have an infinite loop. Thus it supports generation of open intervals with bounds 2 ULP or more apart, or 3 if the bounds span zero to account for -0.0. If you specifically require a value in (0, 1) we could add a specialised version to this sampler to use a faster computation. But the user must be warned that multiplication of (0, 1) by a floating point range can result in a semi-open interval result due to rounding. For example the smallest dyadic rational in 0-1 is 2^-53. Use this to sample from the range (2, 4): jshell | Welcome to JShell -- Version 21.0.9 | For an introduction type: /help intro jshell> 0x1.0p-53 * (4 - 2) + 2 $1 ==> 2.0 This can be avoided using: UniformRandomProvider rng = ... double x = ContinuousUniformSampler.of(rng, 2, 4, true).sample(); If the ultimate requirement is float values in the range (0, 1) then a faster algorithm is possible. But this is not always what the user wants and we should document the possible pitfalls as described above. Alex [1] https://commons.apache.org/proper/commons-rng/commons-rng-docs/apidocs/org/apache/commons/rng/UniformRandomProvider.html [2] https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/random/RandomGenerator.html [3] https://commons.apache.org/proper/commons-rng/commons-rng-docs/apidocs/org/apache/commons/rng/sampling/distribution/ContinuousUniformSampler.html On Thu, 12 Feb 2026 at 13:33, Gilles Sadowski <[email protected]> wrote: > Hello. > > Le jeu. 12 févr. 2026 à 13:54, Jherek Healy > <[email protected]> a écrit : > > > > Dear Commons RNG Team, > > > > I am proposing to introduce a new method in IntProvider and > UniformRandomProvider which computes a random double number in the open > interval (0, 1). > > > > Right now, nextDouble() computes a random double in the semi-closed > interval [0,1). This can be problematic when the random number is to be > used in a inverse distribution function, to provide random numbers > according to a specific distribution, as the inverse distribution function > is only defined on the open interval. > > Is this the sole use-case? > If so, wouldn't it be better (design-wise) to implement the functionality > in the "o.a.c.rng.sampling.distribution" package? > > Regards, > Gilles > > [1] > https://commons.apache.org/proper/commons-rng/commons-rng-sampling/index.html > > > The idea is to match the implementation of > https://www.math.sci.hiroshima-u.ac.jp/m-mat/MT/VERSIONS/C-LANG/mt19937-64.cgenrand64_real3 > > > > double genrand64_real3(void) > > { > > return ((genrand64_int64() >> 12) + 0.5) * (1.0/4503599627370496.0); > > } > > > > There are two possible Java implementations: > > ((nextLong() >>> 12) + 0.5) * 0x1.0p-52; > > or equivalently (reusing the constant used for the semi-closed interval) > > ((v >>> 11) | 1) * * 0x1.0p-53; > > > > Yet another alternvative (which produces different numbers (last digit)) > is the union trick: > > long bits = (random64 >>> 12) | 0x3FF0000000000001L; > > return Double.longBitsToDouble(bits) - 1.0; > > > > I don't have a strong preference in either of the choices. > > > > Jherek > > --------------------------------------------------------------------- > To unsubscribe, e-mail: [email protected] > For additional commands, e-mail: [email protected] > >
