about summary refs log tree commit diff
diff options
context:
space:
mode:
authorHuon Wilson <dbau.pp+github@gmail.com>2013-10-09 02:09:33 +1100
committerHuon Wilson <dbau.pp+github@gmail.com>2013-10-09 22:22:43 +1100
commit38732c4b5cf778cb1b441bfc4290b3e3524b80c2 (patch)
treea1577c0a69f15b8ffc3a06ddd2dcdd773973331d
parenta836f13dc0c2c636fd00b77d05f50a00cc3a7c55 (diff)
downloadrust-38732c4b5cf778cb1b441bfc4290b3e3524b80c2.tar.gz
rust-38732c4b5cf778cb1b441bfc4290b3e3524b80c2.zip
std::rand: Correct the implementation of Rand for f32 & f64.
-rw-r--r--src/libstd/rand/rand_impls.rs37
1 files changed, 29 insertions, 8 deletions
diff --git a/src/libstd/rand/rand_impls.rs b/src/libstd/rand/rand_impls.rs
index 976eea7191a..8ad0bd9e297 100644
--- a/src/libstd/rand/rand_impls.rs
+++ b/src/libstd/rand/rand_impls.rs
@@ -95,23 +95,28 @@ impl Rand for u64 {
 }
 
 impl Rand for f32 {
-    /// A random `f32` in the range `[0, 1)`.
+    /// A random `f32` in the range `[0, 1)`, using 24 bits of
+    /// precision.
     #[inline]
     fn rand<R: Rng>(rng: &mut R) -> f32 {
-        // weird, but this is the easiest way to get 2**32
-        static SCALE: f32 = 2.0 * (1u32 << 31) as f32;
-        rng.next_u32() as f32 / SCALE
+        // 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.
+
+        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)`.
+    /// A random `f64` in the range `[0, 1)`, using 53 bits of
+    /// precision.
     #[inline]
     fn rand<R: Rng>(rng: &mut R) -> f64 {
-        // weird, but this is the easiest way to get 2**64
-        static SCALE: f64 = 2.0 * (1u64 << 63) as f64;
+        // as for f32, but using more bits.
 
-        rng.next_u64() as f64 / SCALE
+        static SCALE: f64 = (1u64 << 53) as f64;
+        (rng.next_u64() >> 11) as f64 / SCALE
     }
 }
 
@@ -198,3 +203,19 @@ impl<T: Rand + 'static> Rand for @T {
     #[inline]
     fn rand<R: Rng>(rng: &mut R) -> @T { @rng.gen() }
 }
+
+#[cfg(test)]
+mod tests {
+    use rand::Rng;
+    struct ConstantRng(u64);
+    impl Rng for ConstantRng {
+        fn next_u64(&mut self) -> u64 {
+            **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)
+    }
+}