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>

Reply via email to