Skip to content

Commit ff72490

Browse files
committed
Implement HighPrecision01 distribution
1 parent b46f8b1 commit ff72490

File tree

3 files changed

+64
-0
lines changed

3 files changed

+64
-0
lines changed

benches/distributions.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ distr!(distr_uniform_codepoint, char, Uniform);
5555

5656
distr!(distr_uniform_f32, f32, Uniform);
5757
distr!(distr_uniform_f64, f64, Uniform);
58+
distr!(distr_high_precision_f32, f32, HighPrecision01);
59+
distr!(distr_high_precision_f64, f64, HighPrecision01);
5860

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

src/distributions/float.rs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ use core::mem;
1414
use Rng;
1515
use distributions::{Distribution, Uniform};
1616

17+
#[derive(Clone, Copy, Debug)]
18+
pub struct HighPrecision01;
19+
1720
pub(crate) trait IntoFloat {
1821
type F;
1922

@@ -64,6 +67,64 @@ macro_rules! float_impls {
6467
fraction.into_float_with_exponent(0) - (1.0 - EPSILON / 2.0)
6568
}
6669
}
70+
71+
impl Distribution<$ty> for HighPrecision01 {
72+
/// Generate a floating point number in the open interval `(0, 1)`
73+
/// (not including either endpoint) with a uniform distribution.
74+
///
75+
/// This is different from `Uniform` in that it it uses all 32 bits
76+
/// of an RNG for a `f32`, instead of only 23, the number of bits
77+
/// that fit in a floats fraction (or 64 instead of 52 bits for a
78+
/// `f64`).
79+
///
80+
/// # Example
81+
/// ```rust
82+
/// use rand::{NewRng, SmallRng, Rng};
83+
/// use rand::distributions::HighPrecision01;
84+
///
85+
/// let val: f32 = SmallRng::new().sample(HighPrecision01);
86+
/// println!("f32 from (0,1): {}", val);
87+
/// ```
88+
///
89+
/// # Algorithm
90+
/// (Note: this description used values that apply to `f32` to
91+
/// illustrate the algorithm).
92+
///
93+
/// The trick to generate a uniform distribution over [0,1) is to
94+
/// set the exponent to the -log2 of the remaining random bits. A
95+
/// simpler alternative to -log2 is to count the number of trailing
96+
/// zero's of the random bits.
97+
///
98+
/// Each exponent is responsible for a piece of the distribution
99+
/// between [0,1). The exponent -1 fills the part [0.5,1). -2 fills
100+
/// [0.25,0.5). The lowest exponent we can get is -10. So a problem
101+
/// with this method is that we can not fill the part between zero
102+
/// and the part from -10. The solution is to treat numbers with an
103+
/// exponent of -10 as if they have -9 as exponent, and substract
104+
/// 2^-9 (implemented in the `fallback` function).
105+
#[inline]
106+
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $ty {
107+
#[inline(never)]
108+
fn fallback(fraction: $uty) -> $ty {
109+
let float_size = (mem::size_of::<$ty>() * 8) as i32;
110+
let min_exponent = $fraction_bits as i32 - float_size;
111+
let adjust = // 2^MIN_EXPONENT
112+
(0 as $uty).into_float_with_exponent(min_exponent);
113+
fraction.into_float_with_exponent(min_exponent) - adjust
114+
}
115+
116+
let fraction_mask = (1 << $fraction_bits) - 1;
117+
let value = rng.$next_u();
118+
119+
let fraction = value & fraction_mask;
120+
let remaining = value >> $fraction_bits;
121+
// If `remaing ==0` we end up in the lowest exponent, which
122+
// needs special treatment.
123+
if remaining == 0 { return fallback(fraction) }
124+
let exp = remaining.trailing_zeros() as i32 + 1;
125+
fraction.into_float_with_exponent(-exp)
126+
}
127+
}
67128
}
68129
}
69130
float_impls! { f32, u32, 23, 127, next_u32 }

src/distributions/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Rng;
1919

2020
pub use self::other::Alphanumeric;
21+
pub use self::float::HighPrecision01;
2122
pub use self::range::Range;
2223
#[cfg(feature="std")]
2324
pub use self::gamma::{Gamma, ChiSquared, FisherF, StudentT};

0 commit comments

Comments
 (0)