Skip to content

std::rand: wrappers for floats from [0,1] and (0,1). #10542

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 19, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 3 additions & 12 deletions src/libstd/rand/distributions/gamma.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

//! The Gamma distribution.

use rand::Rng;
use rand::{Rng, Open01};
use super::{IndependentSample, Sample, StandardNormal, Exp};
use num;

Expand Down Expand Up @@ -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)
}
Expand All @@ -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 ||
Expand Down
10 changes: 6 additions & 4 deletions src/libstd/rand/distributions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 }
Expand Down
40 changes: 40 additions & 0 deletions src/libstd/rand/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down
96 changes: 73 additions & 23 deletions src/libstd/rand/rand_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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 {
Expand All @@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

testing rngs: isn't it wonderful?

// 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);
}
}
}