about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2013-11-19 00:26:27 -0800
committerbors <bors@rust-lang.org>2013-11-19 00:26:27 -0800
commitc4e28ae06842c2066fadb70a1a4f3dc3f1e26e3c (patch)
tree0576e46722adeaa0ab3e8069444e29d8272a15eb
parentf4c22f75d46e94985d2fe45c896bde65e991b13d (diff)
parente6fb622af103a0d642a9b54c78c9183aec5c0d8b (diff)
downloadrust-c4e28ae06842c2066fadb70a1a4f3dc3f1e26e3c.tar.gz
rust-c4e28ae06842c2066fadb70a1a4f3dc3f1e26e3c.zip
auto merge of #10542 : huonw/rust/open01, r=alexcrichton
Provide `Closed01` and `Open01` that generate directly from the
closed/open intervals from 0 to 1, in contrast to the plain impls for
f32 and f64 which generate the half-open [0,1).

Fixes #7755.
-rw-r--r--src/libstd/rand/distributions/gamma.rs15
-rw-r--r--src/libstd/rand/distributions/mod.rs10
-rw-r--r--src/libstd/rand/mod.rs40
-rw-r--r--src/libstd/rand/rand_impls.rs96
4 files changed, 122 insertions, 39 deletions
diff --git a/src/libstd/rand/distributions/gamma.rs b/src/libstd/rand/distributions/gamma.rs
index 735b083df75..7e8c193cb77 100644
--- a/src/libstd/rand/distributions/gamma.rs
+++ b/src/libstd/rand/distributions/gamma.rs
@@ -10,7 +10,7 @@
 
 //! The Gamma distribution.
 
-use rand::Rng;
+use rand::{Rng, Open01};
 use super::{IndependentSample, Sample, StandardNormal, Exp};
 use num;
 
@@ -142,11 +142,7 @@ impl IndependentSample<f64> for Gamma {
 }
 impl IndependentSample<f64> for GammaSmallShape {
     fn ind_sample<R: Rng>(&self, rng: &mut R) -> f64 {
-        // Need (0, 1) here.
-        let mut u = rng.gen::<f64>();
-        while u == 0. {
-            u = rng.gen();
-        }
+        let u = *rng.gen::<Open01<f64>>();
 
         self.large_shape.ind_sample(rng) * num::pow(u, self.inv_shape)
     }
@@ -161,12 +157,7 @@ impl IndependentSample<f64> for GammaLargeShape {
             }
 
             let v = v_cbrt * v_cbrt * v_cbrt;
-            // Need (0, 1) here, not [0, 1). This would be faster if
-            // we were generating an f64 in (0, 1) directly.
-            let mut u = rng.gen::<f64>();
-            while u == 0.0 {
-                u = rng.gen();
-            }
+            let u = *rng.gen::<Open01<f64>>();
 
             let x_sqr = x * x;
             if u < 1.0 - 0.0331 * x_sqr * x_sqr ||
diff --git a/src/libstd/rand/distributions/mod.rs b/src/libstd/rand/distributions/mod.rs
index c8324386470..9697fc22ccd 100644
--- a/src/libstd/rand/distributions/mod.rs
+++ b/src/libstd/rand/distributions/mod.rs
@@ -23,7 +23,7 @@ that do not need to record state.
 use iter::range;
 use option::{Some, None};
 use num;
-use rand::{Rng,Rand};
+use rand::{Rng, Rand, Open01};
 use clone::Clone;
 
 pub use self::range::Range;
@@ -276,10 +276,12 @@ impl Rand for StandardNormal {
             let mut x = 1.0f64;
             let mut y = 0.0f64;
 
-            // FIXME #7755: infinities?
             while -2.0 * y < x * x {
-                x = rng.gen::<f64>().ln() / ziggurat_tables::ZIG_NORM_R;
-                y = rng.gen::<f64>().ln();
+                let x_ = *rng.gen::<Open01<f64>>();
+                let y_ = *rng.gen::<Open01<f64>>();
+
+                x = x_.ln() / ziggurat_tables::ZIG_NORM_R;
+                y = y_.ln();
             }
 
             if u < 0.0 { x - ziggurat_tables::ZIG_NORM_R } else { ziggurat_tables::ZIG_NORM_R - x }
diff --git a/src/libstd/rand/mod.rs b/src/libstd/rand/mod.rs
index e3208981751..a2b555028fa 100644
--- a/src/libstd/rand/mod.rs
+++ b/src/libstd/rand/mod.rs
@@ -647,6 +647,46 @@ pub fn random<T: Rand>() -> T {
     task_rng().gen()
 }
 
+/// A wrapper for generating floating point numbers uniformly in the
+/// open interval `(0,1)` (not including either endpoint).
+///
+/// Use `Closed01` for the closed interval `[0,1]`, and the default
+/// `Rand` implementation for `f32` and `f64` for the half-open
+/// `[0,1)`.
+///
+/// # Example
+/// ```rust
+/// use std::rand::{random, Open01};
+///
+/// fn main() {
+///     println!("f32 from (0,1): {}", *random::<Open01<f32>>());
+///
+///     let x: Open01<f64> = random();
+///     println!("f64 from (0,1): {}", *x);
+/// }
+/// ```
+pub struct Open01<F>(F);
+
+/// A wrapper for generating floating point numbers uniformly in the
+/// closed interval `[0,1]` (including both endpoints).
+///
+/// Use `Open01` for the closed interval `(0,1)`, and the default
+/// `Rand` implementation of `f32` and `f64` for the half-open
+/// `[0,1)`.
+///
+/// # Example
+/// ```rust
+/// use std::rand::{random, Closed01};
+///
+/// fn main() {
+///     println!("f32 from [0,1]: {}", *random::<Closed01<f32>>());
+///
+///     let x: Closed01<f64> = random();
+///     println!("f64 from [0,1]: {}", *x);
+/// }
+/// ```
+pub struct Closed01<F>(F);
+
 #[cfg(test)]
 mod test {
     use iter::{Iterator, range};
diff --git a/src/libstd/rand/rand_impls.rs b/src/libstd/rand/rand_impls.rs
index aad0d4e861c..ff722b739a8 100644
--- a/src/libstd/rand/rand_impls.rs
+++ b/src/libstd/rand/rand_impls.rs
@@ -94,32 +94,52 @@ impl Rand for u64 {
     }
 }
 
-impl Rand for f32 {
-    /// A random `f32` in the range `[0, 1)`, using 24 bits of
-    /// precision.
-    #[inline]
-    fn rand<R: Rng>(rng: &mut R) -> f32 {
-        // using any more than 24 bits will cause (e.g.) 0xffff_ffff
-        // to correspond to 1 exactly, so we need to drop 8 to
-        // guarantee the open end.
+macro_rules! float_impls {
+    ($mod_name:ident, $ty:ty, $mantissa_bits:expr, $method_name:ident, $ignored_bits:expr) => {
+        mod $mod_name {
+            use rand::{Rand, Rng, Open01, Closed01};
 
-        static SCALE: f32 = (1u32 << 24) as f32;
-        (rng.next_u32() >> 8) as f32 / SCALE
-    }
-}
-
-impl Rand for f64 {
-    /// A random `f64` in the range `[0, 1)`, using 53 bits of
-    /// precision.
-    #[inline]
-    fn rand<R: Rng>(rng: &mut R) -> f64 {
-        // as for f32, but using more bits.
+            static SCALE: $ty = (1u64 << $mantissa_bits) as $ty;
 
-        static SCALE: f64 = (1u64 << 53) as f64;
-        (rng.next_u64() >> 11) as f64 / SCALE
+            impl Rand for $ty {
+                /// Generate a floating point number in the half-open
+                /// interval `[0,1)`.
+                ///
+                /// See `Closed01` for the closed interval `[0,1]`,
+                /// and `Open01` for the open interval `(0,1)`.
+                #[inline]
+                fn rand<R: Rng>(rng: &mut R) -> $ty {
+                    // using any more than `mantissa_bits` bits will
+                    // cause (e.g.) 0xffff_ffff to correspond to 1
+                    // exactly, so we need to drop some (8 for f32, 11
+                    // for f64) to guarantee the open end.
+                    (rng.$method_name() >> $ignored_bits) as $ty / SCALE
+                }
+            }
+            impl Rand for Open01<$ty> {
+                #[inline]
+                fn rand<R: Rng>(rng: &mut R) -> Open01<$ty> {
+                    // add a small amount (specifically 2 bits below
+                    // the precision of f64/f32 at 1.0), so that small
+                    // numbers are larger than 0, but large numbers
+                    // aren't pushed to/above 1.
+                    Open01(((rng.$method_name() >> $ignored_bits) as $ty + 0.25) / SCALE)
+                }
+            }
+            impl Rand for Closed01<$ty> {
+                #[inline]
+                fn rand<R: Rng>(rng: &mut R) -> Closed01<$ty> {
+                    // divide by the maximum value of the numerator to
+                    // get a non-zero probability of getting exactly
+                    // 1.0.
+                    Closed01((rng.$method_name() >> $ignored_bits) as $ty / (SCALE - 1.0))
+                }
+            }
+        }
     }
 }
-
+float_impls! { f64_rand_impls, f64, 53, next_u64, 11 }
+float_impls! { f32_rand_impls, f32, 24, next_u32, 8 }
 
 impl Rand for char {
     #[inline]
@@ -206,7 +226,10 @@ impl<T: Rand + 'static> Rand for @T {
 
 #[cfg(test)]
 mod tests {
-    use rand::Rng;
+    use rand::{Rng, task_rng, Open01, Closed01};
+    use iter::range;
+    use option::{None, Some};
+
     struct ConstantRng(u64);
     impl Rng for ConstantRng {
         fn next_u32(&mut self) -> u32 {
@@ -216,9 +239,36 @@ mod tests {
             **self
         }
     }
+
     fn floating_point_edge_cases() {
         // the test for exact equality is correct here.
         assert!(ConstantRng(0xffff_ffff).gen::<f32>() != 1.0)
         assert!(ConstantRng(0xffff_ffff_ffff_ffff).gen::<f64>() != 1.0)
     }
+
+    fn rand_open() {
+        // this is unlikely to catch an incorrect implementation that
+        // generates exactly 0 or 1, but it keeps it sane.
+        let mut rng = task_rng();
+        for _ in range(0, 1_000) {
+            // strict inequalities
+            let f = *rng.gen::<Open01<f64>>();
+            assert!(0.0 < f && f < 1.0);
+
+            let f = *rng.gen::<Open01<f32>>();
+            assert!(0.0 < f && f < 1.0);
+        }
+    }
+
+    fn rand_closed() {
+        let mut rng = task_rng();
+        for _ in range(0, 1_000) {
+            // strict inequalities
+            let f = *rng.gen::<Closed01<f64>>();
+            assert!(0.0 <= f && f <= 1.0);
+
+            let f = *rng.gen::<Closed01<f32>>();
+            assert!(0.0 <= f && f <= 1.0);
+        }
+    }
 }