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.
This commit is contained in:
bors 2013-11-19 00:26:27 -08:00
commit c4e28ae068
4 changed files with 123 additions and 40 deletions

View File

@ -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 ||

View File

@ -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 }

View File

@ -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};

View File

@ -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.
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: $ty = (1u64 << $mantissa_bits) as $ty;
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) -> 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.
static SCALE: f32 = (1u32 << 24) as f32;
(rng.next_u32() >> 8) as f32 / SCALE
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 f64 {
/// A random `f64` in the range `[0, 1)`, using 53 bits of
/// precision.
impl Rand for Open01<$ty> {
#[inline]
fn rand<R: Rng>(rng: &mut R) -> f64 {
// as for f32, but using more bits.
static SCALE: f64 = (1u64 << 53) as f64;
(rng.next_u64() >> 11) as f64 / SCALE
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);
}
}
}