Skip to content

Commit 9c8c4ad

Browse files
committed
Port new Range implementation and only have one uniform float distribution
1 parent 893b50b commit 9c8c4ad

File tree

7 files changed

+568
-301
lines changed

7 files changed

+568
-301
lines changed

benches/distributions.rs

+6-9
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ distr!(distr_range_i64, i64, Range::new(3i64, 12345678901234));
3838
#[cfg(feature = "i128_support")]
3939
distr!(distr_range_i128, i128, Range::new(-12345678901234i128, 12345678901234567890));
4040

41-
distr!(distr_range_float32, f32, Range::new(2.26f32, 2.319));
42-
distr!(distr_range_float, f64, Range::new(2.26f64, 2.319));
41+
distr!(distr_range_f32, f32, Range::new(2.26f32, 2.319));
42+
distr!(distr_range_f64, f64, Range::new(2.26f64, 2.319));
4343

4444
// uniform
4545
distr!(distr_uniform_i8, i8, Uniform);
@@ -52,13 +52,10 @@ distr!(distr_uniform_i128, i128, Uniform);
5252
distr!(distr_uniform_bool, bool, Uniform);
5353
distr!(distr_uniform_codepoint, char, Uniform);
5454

55-
distr!(distr_uniform01_float32, f32, Uniform);
56-
distr!(distr_closed01_float32, f32, Closed01);
57-
distr!(distr_open01_float32, f32, Open01);
58-
59-
distr!(distr_uniform01_float, f64, Uniform);
60-
distr!(distr_closed01_float, f64, Closed01);
61-
distr!(distr_open01_float, f64, Open01);
55+
distr!(distr_uniform_f32, f32, Uniform);
56+
distr!(distr_uniform_f64, f64, Uniform);
57+
distr!(distr_accurate_f32, f32, Accurate);
58+
distr!(distr_accurate_f64, f64, Accurate);
6259

6360
// distributions
6461
distr!(distr_exp, f64, Exp::new(2.71828 * 3.14159));

src/distributions/float.rs

+61-147
Original file line numberDiff line numberDiff line change
@@ -9,176 +9,90 @@
99
// except according to those terms.
1010

1111
//! Basic floating-point number distributions
12-
13-
14-
/// A distribution to sample floating point numbers uniformly in the open
15-
/// interval `(0, 1)` (not including either endpoint).
16-
///
17-
/// See also: [`Closed01`] for the closed `[0, 1]`; [`Uniform`] for the
18-
/// half-open `[0, 1)`.
19-
///
20-
/// # Example
21-
/// ```rust
22-
/// use rand::{weak_rng, Rng};
23-
/// use rand::distributions::Open01;
24-
///
25-
/// let val: f32 = weak_rng().sample(Open01);
26-
/// println!("f32 from (0,1): {}", val);
27-
/// ```
28-
///
29-
/// [`Uniform`]: struct.Uniform.html
30-
/// [`Closed01`]: struct.Closed01.html
31-
#[derive(Clone, Copy, Debug)]
32-
pub struct Open01;
33-
34-
/// A distribution to sample floating point numbers uniformly in the closed
35-
/// interval `[0, 1]` (including both endpoints).
36-
///
37-
/// See also: [`Open01`] for the open `(0, 1)`; [`Uniform`] for the half-open
38-
/// `[0, 1)`.
39-
///
40-
/// # Example
41-
/// ```rust
42-
/// use rand::{weak_rng, Rng};
43-
/// use rand::distributions::Closed01;
44-
///
45-
/// let val: f32 = weak_rng().sample(Closed01);
46-
/// println!("f32 from [0,1]: {}", val);
47-
/// ```
48-
///
49-
/// [`Uniform`]: struct.Uniform.html
50-
/// [`Open01`]: struct.Open01.html
51-
#[derive(Clone, Copy, Debug)]
52-
pub struct Closed01;
53-
12+
use Rng;
13+
use distributions::{Distribution, Uniform};
14+
use ::core::mem;
15+
16+
pub(crate) trait IntoFloat {
17+
type F;
18+
19+
/// Helper method to combine the fraction and a contant exponent into a
20+
/// float.
21+
///
22+
/// Only the least significant bits of `self` may be set, 23 for `f32` and
23+
/// 52 for `f64`.
24+
/// The resulting value will fall in a range that depends on the exponent.
25+
/// As an example the range with exponent 0 will be
26+
/// [2<sup>0</sup>..2<sup>1</sup>-1), which is [1..2).
27+
#[inline(always)]
28+
fn into_float_with_exponent(self, exponent: i32) -> Self::F;
29+
}
5430

5531
macro_rules! float_impls {
56-
($mod_name:ident, $ty:ty, $mantissa_bits:expr, $method_name:ident) => {
57-
mod $mod_name {
58-
use Rng;
59-
use distributions::{Distribution, Uniform};
60-
use super::{Open01, Closed01};
61-
62-
const SCALE: $ty = (1u64 << $mantissa_bits) as $ty;
63-
64-
impl Distribution<$ty> for Uniform {
65-
/// Generate a floating point number in the half-open
66-
/// interval `[0,1)`.
67-
///
68-
/// See `Closed01` for the closed interval `[0,1]`,
69-
/// and `Open01` for the open interval `(0,1)`.
70-
#[inline]
71-
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $ty {
72-
rng.$method_name()
73-
}
74-
}
75-
impl Distribution<$ty> for Open01 {
76-
#[inline]
77-
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $ty {
78-
// add 0.5 * epsilon, so that smallest number is
79-
// greater than 0, and largest number is still
80-
// less than 1, specifically 1 - 0.5 * epsilon.
81-
rng.$method_name() + 0.5 / SCALE
82-
}
32+
($ty:ty, $uty:ty, $fraction_bits:expr, $exponent_bias:expr,
33+
$next_u:ident) => {
34+
impl IntoFloat for $uty {
35+
type F = $ty;
36+
#[inline(always)]
37+
fn into_float_with_exponent(self, exponent: i32) -> $ty {
38+
// The exponent is encoded using an offset-binary representation,
39+
// with the zero offset being 127
40+
let exponent_bits = (($exponent_bias + exponent) as $uty) << $fraction_bits;
41+
unsafe { mem::transmute(self | exponent_bits) }
8342
}
84-
impl Distribution<$ty> for Closed01 {
85-
#[inline]
86-
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $ty {
87-
// rescale so that 1.0 - epsilon becomes 1.0
88-
// precisely.
89-
rng.$method_name() * SCALE / (SCALE - 1.0)
90-
}
43+
}
44+
45+
impl Distribution<$ty> for Uniform {
46+
/// Generate a floating point number in the open interval `(0, 1)`
47+
/// (not including either endpoint) with a uniform distribution.
48+
///
49+
/// # Example
50+
/// ```rust
51+
/// use rand::{weak_rng, Rng};
52+
/// use rand::distributions::Uniform;
53+
///
54+
/// let val: f32 = weak_rng().sample(Uniform);
55+
/// println!("f32 from (0,1): {}", val);
56+
/// ```
57+
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $ty {
58+
const EPSILON: $ty = 1.0 / (1u64 << $fraction_bits) as $ty;
59+
let float_size = mem::size_of::<$ty>() * 8;
60+
61+
let value = rng.$next_u();
62+
let fraction = value >> (float_size - $fraction_bits);
63+
fraction.into_float_with_exponent(0) - (1.0 - EPSILON / 2.0)
9164
}
9265
}
9366
}
9467
}
95-
float_impls! { f64_rand_impls, f64, 52, next_f64 }
96-
float_impls! { f32_rand_impls, f32, 23, next_f32 }
68+
float_impls! { f32, u32, 23, 127, next_u32 }
69+
float_impls! { f64, u64, 52, 1023, next_u64 }
9770

9871

9972
#[cfg(test)]
10073
mod tests {
10174
use Rng;
10275
use mock::StepRng;
103-
use distributions::{Open01, Closed01};
10476

10577
const EPSILON32: f32 = ::core::f32::EPSILON;
10678
const EPSILON64: f64 = ::core::f64::EPSILON;
10779

10880
#[test]
10981
fn floating_point_edge_cases() {
11082
let mut zeros = StepRng::new(0, 0);
111-
assert_eq!(zeros.gen::<f32>(), 0.0);
112-
assert_eq!(zeros.gen::<f64>(), 0.0);
113-
114-
let mut one = StepRng::new(1, 0);
115-
assert_eq!(one.gen::<f32>(), EPSILON32);
116-
assert_eq!(one.gen::<f64>(), EPSILON64);
117-
118-
let mut max = StepRng::new(!0, 0);
119-
assert_eq!(max.gen::<f32>(), 1.0 - EPSILON32);
120-
assert_eq!(max.gen::<f64>(), 1.0 - EPSILON64);
121-
}
83+
assert_eq!(zeros.gen::<f32>(), 0.0 + EPSILON32 / 2.0);
84+
assert_eq!(zeros.gen::<f64>(), 0.0 + EPSILON64 / 2.0);
12285

123-
#[test]
124-
fn fp_closed_edge_cases() {
125-
let mut zeros = StepRng::new(0, 0);
126-
assert_eq!(zeros.sample::<f32, _>(Closed01), 0.0);
127-
assert_eq!(zeros.sample::<f64, _>(Closed01), 0.0);
128-
129-
let mut one = StepRng::new(1, 0);
130-
let one32 = one.sample::<f32, _>(Closed01);
131-
let one64 = one.sample::<f64, _>(Closed01);
132-
assert!(EPSILON32 < one32 && one32 < EPSILON32 * 1.01);
133-
assert!(EPSILON64 < one64 && one64 < EPSILON64 * 1.01);
134-
135-
let mut max = StepRng::new(!0, 0);
136-
assert_eq!(max.sample::<f32, _>(Closed01), 1.0);
137-
assert_eq!(max.sample::<f64, _>(Closed01), 1.0);
138-
}
139-
140-
#[test]
141-
fn fp_open_edge_cases() {
142-
let mut zeros = StepRng::new(0, 0);
143-
assert_eq!(zeros.sample::<f32, _>(Open01), 0.0 + EPSILON32 / 2.0);
144-
assert_eq!(zeros.sample::<f64, _>(Open01), 0.0 + EPSILON64 / 2.0);
145-
146-
let mut one = StepRng::new(1, 0);
147-
let one32 = one.sample::<f32, _>(Open01);
148-
let one64 = one.sample::<f64, _>(Open01);
86+
let mut one = StepRng::new(1 << 9, 0);
87+
let one32 = one.gen::<f32>();
14988
assert!(EPSILON32 < one32 && one32 < EPSILON32 * 2.0);
150-
assert!(EPSILON64 < one64 && one64 < EPSILON64 * 2.0);
151-
152-
let mut max = StepRng::new(!0, 0);
153-
assert_eq!(max.sample::<f32, _>(Open01), 1.0 - EPSILON32 / 2.0);
154-
assert_eq!(max.sample::<f64, _>(Open01), 1.0 - EPSILON64 / 2.0);
155-
}
156-
157-
#[test]
158-
fn rand_open() {
159-
// this is unlikely to catch an incorrect implementation that
160-
// generates exactly 0 or 1, but it keeps it sane.
161-
let mut rng = ::test::rng(510);
162-
for _ in 0..1_000 {
163-
// strict inequalities
164-
let f: f64 = rng.sample(Open01);
165-
assert!(0.0 < f && f < 1.0);
16689

167-
let f: f32 = rng.sample(Open01);
168-
assert!(0.0 < f && f < 1.0);
169-
}
170-
}
171-
172-
#[test]
173-
fn rand_closed() {
174-
let mut rng = ::test::rng(511);
175-
for _ in 0..1_000 {
176-
// strict inequalities
177-
let f: f64 = rng.sample(Closed01);
178-
assert!(0.0 <= f && f <= 1.0);
90+
let mut one = StepRng::new(1 << 12, 0);
91+
let one64 = one.gen::<f64>();
92+
assert!(EPSILON64 < one64 && one64 < EPSILON64 * 2.0);
17993

180-
let f: f32 = rng.sample(Closed01);
181-
assert!(0.0 <= f && f <= 1.0);
182-
}
94+
let mut max = StepRng::new(!0, 0);
95+
assert_eq!(max.gen::<f32>(), 1.0 - EPSILON32 / 2.0);
96+
assert_eq!(max.gen::<f64>(), 1.0 - EPSILON64 / 2.0);
18397
}
18498
}

src/distributions/gamma.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use self::ChiSquaredRepr::*;
1717

1818
use {Rng};
1919
use distributions::normal::StandardNormal;
20-
use distributions::{Distribution, Exp, Open01};
20+
use distributions::{Distribution, Exp, Uniform};
2121

2222
/// The Gamma distribution `Gamma(shape, scale)` distribution.
2323
///
@@ -144,7 +144,7 @@ impl Distribution<f64> for Gamma {
144144
}
145145
impl Distribution<f64> for GammaSmallShape {
146146
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> f64 {
147-
let u: f64 = rng.sample(Open01);
147+
let u: f64 = rng.sample(Uniform);
148148

149149
self.large_shape.sample(rng) * u.powf(self.inv_shape)
150150
}
@@ -159,7 +159,7 @@ impl Distribution<f64> for GammaLargeShape {
159159
}
160160

161161
let v = v_cbrt * v_cbrt * v_cbrt;
162-
let u: f64 = rng.sample(Open01);
162+
let u: f64 = rng.sample(Uniform);
163163

164164
let x_sqr = x * x;
165165
if u < 1.0 - 0.0331 * x_sqr * x_sqr ||

0 commit comments

Comments
 (0)