mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-27 01:04:03 +00:00
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:
commit
c4e28ae068
@ -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 ||
|
||||
|
@ -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 }
|
||||
|
@ -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};
|
||||
|
@ -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
|
||||
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) -> $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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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: f64 = (1u64 << 53) as f64;
|
||||
(rng.next_u64() >> 11) as f64 / SCALE
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user