@@ -97,6 +97,35 @@ impl<F: HPFloatHelper> HighPrecision<F> {
97
97
}
98
98
}
99
99
100
+ /// Generate a floating point number in the half-open interval `[0, 1)` with a
101
+ /// uniform distribution.
102
+ ///
103
+ /// This is different from `Uniform` in that it uses all 32 bits of an RNG for a
104
+ /// `f32`, instead of only 23, the number of bits that fit in a floats fraction
105
+ /// (or 64 instead of 52 bits for a `f64`).
106
+ ///
107
+ /// The smallest interval between values that can be generated is 2^-32
108
+ /// (2.3283064e-10) for `f32`, and 2^-64 (5.421010862427522e-20) for `f64`.
109
+ /// But this interval increases further away from zero because of limitations of
110
+ /// the floating point format. Close to 1.0 the interval is 2^-24 (5.9604645e-8)
111
+ /// for `f32`, and 2^-53 (1.1102230246251565) for `f64`. Compare this with
112
+ /// `Uniform`, which has a fixed interval of 2^23 and 2^-52 respectively.
113
+ ///
114
+ /// Note: in the future this may change change to request even more bits from
115
+ /// the RNG if the value gets very close to 0.0, so it always has as many digits
116
+ /// of precision as the float can represent.
117
+ ///
118
+ /// # Example
119
+ /// ```rust
120
+ /// use rand::{NewRng, SmallRng, Rng};
121
+ /// use rand::distributions::HighPrecision01;
122
+ ///
123
+ /// let val: f32 = SmallRng::new().sample(HighPrecision01);
124
+ /// println!("f32 from [0,1): {}", val);
125
+ /// ```
126
+ #[ derive( Clone , Copy , Debug ) ]
127
+ pub struct HighPrecision01 ;
128
+
100
129
pub ( crate ) trait IntoFloat {
101
130
type F ;
102
131
@@ -201,6 +230,64 @@ float_impls! { f64x8, u64x8, f64, u64, 52, 1023 }
201
230
202
231
macro_rules! high_precision_float_impls {
203
232
( $ty: ty, $uty: ty, $ity: ty, $fraction_bits: expr, $exponent_bits: expr) => {
233
+ impl Distribution <$ty> for HighPrecision01 {
234
+ /// Generate a floating point number in the half-open interval
235
+ /// `[0, 1)` with a uniform distribution.
236
+ ///
237
+ /// This is different from `Uniform` in that it uses all 32 bits
238
+ /// of an RNG for a `f32`, instead of only 23, the number of bits
239
+ /// that fit in a floats fraction (or 64 instead of 52 bits for a
240
+ /// `f64`).
241
+ ///
242
+ /// # Example
243
+ /// ```rust
244
+ /// use rand::{NewRng, SmallRng, Rng};
245
+ /// use rand::distributions::HighPrecision01;
246
+ ///
247
+ /// let val: f32 = SmallRng::new().sample(HighPrecision01);
248
+ /// println!("f32 from [0,1): {}", val);
249
+ /// ```
250
+ ///
251
+ /// # Algorithm
252
+ /// (Note: this description used values that apply to `f32` to
253
+ /// illustrate the algorithm).
254
+ ///
255
+ /// The trick to generate a uniform distribution over [0,1) is to
256
+ /// set the exponent to the -log2 of the remaining random bits. A
257
+ /// simpler alternative to -log2 is to count the number of trailing
258
+ /// zero's of the random bits.
259
+ ///
260
+ /// Each exponent is responsible for a piece of the distribution
261
+ /// between [0,1). The exponent -1 fills the part [0.5,1). -2 fills
262
+ /// [0.25,0.5). The lowest exponent we can get is -10. So a problem
263
+ /// with this method is that we can not fill the part between zero
264
+ /// and the part from -10. The solution is to treat numbers with an
265
+ /// exponent of -10 as if they have -9 as exponent, and substract
266
+ /// 2^-9 (implemented in the `fallback` function).
267
+ #[ inline]
268
+ fn sample<R : Rng + ?Sized >( & self , rng: & mut R ) -> $ty {
269
+ #[ inline( never) ]
270
+ fn fallback( fraction: $uty) -> $ty {
271
+ let float_size = ( mem:: size_of:: <$ty>( ) * 8 ) as i32 ;
272
+ let min_exponent = $fraction_bits as i32 - float_size;
273
+ let adjust = // 2^MIN_EXPONENT
274
+ ( 0 as $uty) . into_float_with_exponent( min_exponent) ;
275
+ fraction. into_float_with_exponent( min_exponent) - adjust
276
+ }
277
+
278
+ let fraction_mask = ( 1 << $fraction_bits) - 1 ;
279
+ let value: $uty = rng. gen ( ) ;
280
+
281
+ let fraction = value & fraction_mask;
282
+ let remaining = value >> $fraction_bits;
283
+ // If `remaing ==0` we end up in the lowest exponent, which
284
+ // needs special treatment.
285
+ if remaining == 0 { return fallback( fraction) }
286
+ let exp = remaining. trailing_zeros( ) as i32 + 1 ;
287
+ fraction. into_float_with_exponent( -exp)
288
+ }
289
+ }
290
+
204
291
impl HPFloatHelper for $ty {
205
292
type SignedInt = $ity;
206
293
type SignedIntDistribution = <$ity as SampleUniform >:: Sampler ;
@@ -628,4 +715,18 @@ mod tests {
628
715
] ) ;
629
716
}
630
717
}
718
+
719
+ #[ test]
720
+ fn high_precision_01_edge_cases ( ) {
721
+ // Test that the distribution is a half-open range over [0,1).
722
+ // These constants happen to generate the lowest and highest floats in
723
+ // the range.
724
+ let mut zeros = StepRng :: new ( 0 , 0 ) ;
725
+ assert_eq ! ( zeros. sample:: <f32 , _>( HighPrecision01 ) , 0.0 ) ;
726
+ assert_eq ! ( zeros. sample:: <f64 , _>( HighPrecision01 ) , 0.0 ) ;
727
+
728
+ let mut ones = StepRng :: new ( 0xffff_ffff_ffff_ffff , 0 ) ;
729
+ assert_eq ! ( ones. sample:: <f32 , _>( HighPrecision01 ) , 0.99999994 ) ;
730
+ assert_eq ! ( ones. sample:: <f64 , _>( HighPrecision01 ) , 0.9999999999999999 ) ;
731
+ }
631
732
}
0 commit comments