This is an automated email from the ASF dual-hosted git repository.
aherbert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-rng.git
The following commit(s) were added to refs/heads/master by this push:
new e89f37c0 RNG-190: Special case for open interval (0, 1)
e89f37c0 is described below
commit e89f37c011323587927f9f3ac701305e465e91da
Author: Alex Herbert <[email protected]>
AuthorDate: Sun Feb 15 11:27:09 2026 +0000
RNG-190: Special case for open interval (0, 1)
---
.../distribution/ContinuousUniformSampler.java | 38 ++++++++++++++++--
.../distribution/ContinuousUniformSamplerTest.java | 46 +++++++++++++++-------
src/changes/changes.xml | 4 ++
3 files changed, 71 insertions(+), 17 deletions(-)
diff --git
a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/ContinuousUniformSampler.java
b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/ContinuousUniformSampler.java
index ee4031b7..a5f3e463 100644
---
a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/ContinuousUniformSampler.java
+++
b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/ContinuousUniformSampler.java
@@ -33,15 +33,17 @@ public class ContinuousUniformSampler
/** The minimum ULP gap for the open interval when the doubles have the
opposite sign. */
private static final int MIN_ULP_OPPOSITE_SIGN = 3;
+ /** Underlying source of randomness. */
+ protected final UniformRandomProvider rng;
/** Lower bound. */
private final double lo;
/** Higher bound. */
private final double hi;
- /** Underlying source of randomness. */
- private final UniformRandomProvider rng;
/**
- * Specialization to sample from an open interval {@code (lo, hi)}.
+ * Specialization to sample from an open interval {@code (lo, hi)} (see
RNG-145).
+ *
+ * @since 1.4
*/
private static final class OpenIntervalContinuousUniformSampler extends
ContinuousUniformSampler {
/**
@@ -71,6 +73,32 @@ public class ContinuousUniformSampler
}
}
+ /**
+ * Specialization to sample from an open interval {@code (0, 1)} (see
RNG-190).
+ *
+ * @since 1.7
+ */
+ private static final class OpenIntervalContinuousUniformSampler01 extends
ContinuousUniformSampler {
+ /**
+ * @param rng Generator of uniformly distributed random numbers.
+ */
+ OpenIntervalContinuousUniformSampler01(UniformRandomProvider rng) {
+ super(rng, 0, 1);
+ }
+
+ @Override
+ public double sample() {
+ // Adding a least significant bit before conversion to float.
+ // Output is 2^52 possible rationals.
+ return ((rng.nextLong() >>> 11) | 1L) * 0x1.0p-53d;
+ }
+
+ @Override
+ public SharedStateContinuousSampler
withUniformRandomProvider(UniformRandomProvider rng) {
+ return new OpenIntervalContinuousUniformSampler01(rng);
+ }
+ }
+
/**
* Create an instance.
*
@@ -171,6 +199,10 @@ public class ContinuousUniformSampler
throw new IllegalArgumentException("Invalid open interval (" +
lo + "," + hi + ")");
}
+ // Special case for (0, 1)
+ if (lo == 0 && hi == 1) {
+ return new OpenIntervalContinuousUniformSampler01(rng);
+ }
return new OpenIntervalContinuousUniformSampler(rng, lo, hi);
}
return new ContinuousUniformSampler(rng, lo, hi);
diff --git
a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/ContinuousUniformSamplerTest.java
b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/ContinuousUniformSamplerTest.java
index cf84463d..43db611e 100644
---
a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/ContinuousUniformSamplerTest.java
+++
b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/ContinuousUniformSamplerTest.java
@@ -21,6 +21,8 @@ import org.apache.commons.rng.core.source64.SplitMix64;
import org.apache.commons.rng.sampling.RandomAssert;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
/**
* Test for the {@link ContinuousUniformSampler}.
@@ -53,8 +55,14 @@ class ContinuousUniformSamplerTest {
* Test the sampler excludes the bounds when the underlying generator
returns long values
* that produce the limit of the uniform double output.
*/
- @Test
- void testExcludeBounds() {
+ @ParameterizedTest
+ @CsvSource({
+ "3.18, 5.23",
+ "0, 1",
+ "0, 2",
+ "1, 2",
+ })
+ void testExcludeBounds(double low, double high) {
// A broken RNG that will return in an alternating sequence from 0 up
or -1 down.
// This is either zero bits or all the bits
final UniformRandomProvider rng = new SplitMix64(0L) {
@@ -76,8 +84,6 @@ class ContinuousUniformSamplerTest {
return x << 11;
}
};
- final double low = 3.18;
- final double high = 5.23;
final SharedStateContinuousSampler sampler =
ContinuousUniformSampler.of(rng, low, high, true);
// Test the sampler excludes the end points
@@ -155,20 +161,34 @@ class ContinuousUniformSamplerTest {
}
/**
- * Test the SharedStateSampler implementation.
+ * Test open interval {@code (0, 1)} is within 1 dyadic rational of next
double.
*/
@Test
- void testSharedStateSampler() {
- testSharedStateSampler(false);
- testSharedStateSampler(true);
+ void testOpenInterval01Sample() {
+ final UniformRandomProvider rng1 = RandomAssert.seededRNG();
+ final UniformRandomProvider rng2 = RandomAssert.seededRNG();
+
+ final SharedStateContinuousSampler sampler =
+ ContinuousUniformSampler.of(rng2, 0, 1, true);
+
+ for (int i = 0; i < 20; i++) {
+ Assertions.assertEquals(rng1.nextDouble(), sampler.sample(),
0x1.0p-53);
+ }
}
/**
* Test the SharedStateSampler implementation.
- *
- * @param excludedBounds Set to true to exclude the bounds.
*/
- private static void testSharedStateSampler(boolean excludedBounds) {
+ @ParameterizedTest
+ @CsvSource({
+ "3.18, 5.23, false",
+ "0, 1, false",
+ "0, 2, false",
+ "3.18, 5.23, true",
+ "0, 1, true",
+ "0, 2, true",
+ })
+ void testSharedStateSampler(double low, double high, boolean
excludeBounds) {
// Create RNGs that will generate a sample at the limits.
// This tests the bounds excluded sampler correctly shares state.
// Do this using a RNG that outputs 0 for the first nextDouble().
@@ -190,10 +210,8 @@ class ContinuousUniformSamplerTest {
return y;
}
};
- final double low = 1.23;
- final double high = 4.56;
final SharedStateContinuousSampler sampler1 =
- ContinuousUniformSampler.of(rng1, low, high, excludedBounds);
+ ContinuousUniformSampler.of(rng1, low, high, excludeBounds);
final SharedStateContinuousSampler sampler2 =
sampler1.withUniformRandomProvider(rng2);
RandomAssert.assertProduceSameSequence(sampler1, sampler2);
}
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 3744557f..8798b3c9 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -56,6 +56,10 @@ If the output is not quite correct, check for invisible
trailing spaces!
<release version="1.7" date="TBD" description="
New features, updates and bug fixes (requires Java 8).
">
+ <action dev="aherbert" type="update" issue="RNG-190">
+ "ContinuousUniformSampler": Add a special case for the open interval
(0, 1)
+ with increased performance.
+ </action>
<action dev="aherbert" due-to="Jherek Healy" type="add" issue="RNG-188">
Add Philox4x32 and Philox4x64 random number generators.
</action>