Skip to content

Commit e6fb622

Browse files
committed
std::rand: wrappers for floats from [0,1] and (0,1).
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.
1 parent 4dded43 commit e6fb622

File tree

4 files changed

+122
-39
lines changed

4 files changed

+122
-39
lines changed

src/libstd/rand/distributions/gamma.rs

+3-12
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
//! The Gamma distribution.
1212
13-
use rand::Rng;
13+
use rand::{Rng, Open01};
1414
use super::{IndependentSample, Sample, StandardNormal, Exp};
1515
use num;
1616

@@ -142,11 +142,7 @@ impl IndependentSample<f64> for Gamma {
142142
}
143143
impl IndependentSample<f64> for GammaSmallShape {
144144
fn ind_sample<R: Rng>(&self, rng: &mut R) -> f64 {
145-
// Need (0, 1) here.
146-
let mut u = rng.gen::<f64>();
147-
while u == 0. {
148-
u = rng.gen();
149-
}
145+
let u = *rng.gen::<Open01<f64>>();
150146

151147
self.large_shape.ind_sample(rng) * num::pow(u, self.inv_shape)
152148
}
@@ -161,12 +157,7 @@ impl IndependentSample<f64> for GammaLargeShape {
161157
}
162158

163159
let v = v_cbrt * v_cbrt * v_cbrt;
164-
// Need (0, 1) here, not [0, 1). This would be faster if
165-
// we were generating an f64 in (0, 1) directly.
166-
let mut u = rng.gen::<f64>();
167-
while u == 0.0 {
168-
u = rng.gen();
169-
}
160+
let u = *rng.gen::<Open01<f64>>();
170161

171162
let x_sqr = x * x;
172163
if u < 1.0 - 0.0331 * x_sqr * x_sqr ||

src/libstd/rand/distributions/mod.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ that do not need to record state.
2323
use iter::range;
2424
use option::{Some, None};
2525
use num;
26-
use rand::{Rng,Rand};
26+
use rand::{Rng, Rand, Open01};
2727
use clone::Clone;
2828

2929
pub use self::range::Range;
@@ -276,10 +276,12 @@ impl Rand for StandardNormal {
276276
let mut x = 1.0f64;
277277
let mut y = 0.0f64;
278278

279-
// FIXME #7755: infinities?
280279
while -2.0 * y < x * x {
281-
x = rng.gen::<f64>().ln() / ziggurat_tables::ZIG_NORM_R;
282-
y = rng.gen::<f64>().ln();
280+
let x_ = *rng.gen::<Open01<f64>>();
281+
let y_ = *rng.gen::<Open01<f64>>();
282+
283+
x = x_.ln() / ziggurat_tables::ZIG_NORM_R;
284+
y = y_.ln();
283285
}
284286

285287
if u < 0.0 { x - ziggurat_tables::ZIG_NORM_R } else { ziggurat_tables::ZIG_NORM_R - x }

src/libstd/rand/mod.rs

+40
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,46 @@ pub fn random<T: Rand>() -> T {
647647
task_rng().gen()
648648
}
649649

650+
/// A wrapper for generating floating point numbers uniformly in the
651+
/// open interval `(0,1)` (not including either endpoint).
652+
///
653+
/// Use `Closed01` for the closed interval `[0,1]`, and the default
654+
/// `Rand` implementation for `f32` and `f64` for the half-open
655+
/// `[0,1)`.
656+
///
657+
/// # Example
658+
/// ```rust
659+
/// use std::rand::{random, Open01};
660+
///
661+
/// fn main() {
662+
/// println!("f32 from (0,1): {}", *random::<Open01<f32>>());
663+
///
664+
/// let x: Open01<f64> = random();
665+
/// println!("f64 from (0,1): {}", *x);
666+
/// }
667+
/// ```
668+
pub struct Open01<F>(F);
669+
670+
/// A wrapper for generating floating point numbers uniformly in the
671+
/// closed interval `[0,1]` (including both endpoints).
672+
///
673+
/// Use `Open01` for the closed interval `(0,1)`, and the default
674+
/// `Rand` implementation of `f32` and `f64` for the half-open
675+
/// `[0,1)`.
676+
///
677+
/// # Example
678+
/// ```rust
679+
/// use std::rand::{random, Closed01};
680+
///
681+
/// fn main() {
682+
/// println!("f32 from [0,1]: {}", *random::<Closed01<f32>>());
683+
///
684+
/// let x: Closed01<f64> = random();
685+
/// println!("f64 from [0,1]: {}", *x);
686+
/// }
687+
/// ```
688+
pub struct Closed01<F>(F);
689+
650690
#[cfg(test)]
651691
mod test {
652692
use iter::{Iterator, range};

src/libstd/rand/rand_impls.rs

+73-23
Original file line numberDiff line numberDiff line change
@@ -94,32 +94,52 @@ impl Rand for u64 {
9494
}
9595
}
9696

97-
impl Rand for f32 {
98-
/// A random `f32` in the range `[0, 1)`, using 24 bits of
99-
/// precision.
100-
#[inline]
101-
fn rand<R: Rng>(rng: &mut R) -> f32 {
102-
// using any more than 24 bits will cause (e.g.) 0xffff_ffff
103-
// to correspond to 1 exactly, so we need to drop 8 to
104-
// guarantee the open end.
97+
macro_rules! float_impls {
98+
($mod_name:ident, $ty:ty, $mantissa_bits:expr, $method_name:ident, $ignored_bits:expr) => {
99+
mod $mod_name {
100+
use rand::{Rand, Rng, Open01, Closed01};
105101

106-
static SCALE: f32 = (1u32 << 24) as f32;
107-
(rng.next_u32() >> 8) as f32 / SCALE
108-
}
109-
}
110-
111-
impl Rand for f64 {
112-
/// A random `f64` in the range `[0, 1)`, using 53 bits of
113-
/// precision.
114-
#[inline]
115-
fn rand<R: Rng>(rng: &mut R) -> f64 {
116-
// as for f32, but using more bits.
102+
static SCALE: $ty = (1u64 << $mantissa_bits) as $ty;
117103

118-
static SCALE: f64 = (1u64 << 53) as f64;
119-
(rng.next_u64() >> 11) as f64 / SCALE
104+
impl Rand for $ty {
105+
/// Generate a floating point number in the half-open
106+
/// interval `[0,1)`.
107+
///
108+
/// See `Closed01` for the closed interval `[0,1]`,
109+
/// and `Open01` for the open interval `(0,1)`.
110+
#[inline]
111+
fn rand<R: Rng>(rng: &mut R) -> $ty {
112+
// using any more than `mantissa_bits` bits will
113+
// cause (e.g.) 0xffff_ffff to correspond to 1
114+
// exactly, so we need to drop some (8 for f32, 11
115+
// for f64) to guarantee the open end.
116+
(rng.$method_name() >> $ignored_bits) as $ty / SCALE
117+
}
118+
}
119+
impl Rand for Open01<$ty> {
120+
#[inline]
121+
fn rand<R: Rng>(rng: &mut R) -> Open01<$ty> {
122+
// add a small amount (specifically 2 bits below
123+
// the precision of f64/f32 at 1.0), so that small
124+
// numbers are larger than 0, but large numbers
125+
// aren't pushed to/above 1.
126+
Open01(((rng.$method_name() >> $ignored_bits) as $ty + 0.25) / SCALE)
127+
}
128+
}
129+
impl Rand for Closed01<$ty> {
130+
#[inline]
131+
fn rand<R: Rng>(rng: &mut R) -> Closed01<$ty> {
132+
// divide by the maximum value of the numerator to
133+
// get a non-zero probability of getting exactly
134+
// 1.0.
135+
Closed01((rng.$method_name() >> $ignored_bits) as $ty / (SCALE - 1.0))
136+
}
137+
}
138+
}
120139
}
121140
}
122-
141+
float_impls! { f64_rand_impls, f64, 53, next_u64, 11 }
142+
float_impls! { f32_rand_impls, f32, 24, next_u32, 8 }
123143

124144
impl Rand for char {
125145
#[inline]
@@ -206,7 +226,10 @@ impl<T: Rand + 'static> Rand for @T {
206226

207227
#[cfg(test)]
208228
mod tests {
209-
use rand::Rng;
229+
use rand::{Rng, task_rng, Open01, Closed01};
230+
use iter::range;
231+
use option::{None, Some};
232+
210233
struct ConstantRng(u64);
211234
impl Rng for ConstantRng {
212235
fn next_u32(&mut self) -> u32 {
@@ -216,9 +239,36 @@ mod tests {
216239
**self
217240
}
218241
}
242+
219243
fn floating_point_edge_cases() {
220244
// the test for exact equality is correct here.
221245
assert!(ConstantRng(0xffff_ffff).gen::<f32>() != 1.0)
222246
assert!(ConstantRng(0xffff_ffff_ffff_ffff).gen::<f64>() != 1.0)
223247
}
248+
249+
fn rand_open() {
250+
// this is unlikely to catch an incorrect implementation that
251+
// generates exactly 0 or 1, but it keeps it sane.
252+
let mut rng = task_rng();
253+
for _ in range(0, 1_000) {
254+
// strict inequalities
255+
let f = *rng.gen::<Open01<f64>>();
256+
assert!(0.0 < f && f < 1.0);
257+
258+
let f = *rng.gen::<Open01<f32>>();
259+
assert!(0.0 < f && f < 1.0);
260+
}
261+
}
262+
263+
fn rand_closed() {
264+
let mut rng = task_rng();
265+
for _ in range(0, 1_000) {
266+
// strict inequalities
267+
let f = *rng.gen::<Closed01<f64>>();
268+
assert!(0.0 <= f && f <= 1.0);
269+
270+
let f = *rng.gen::<Closed01<f32>>();
271+
assert!(0.0 <= f && f <= 1.0);
272+
}
273+
}
224274
}

0 commit comments

Comments
 (0)