@@ -14,6 +14,9 @@ use core::mem;
14
14
use Rng ;
15
15
use distributions:: { Distribution , Uniform } ;
16
16
17
+ #[ derive( Clone , Copy , Debug ) ]
18
+ pub struct HighPrecision01 ;
19
+
17
20
pub ( crate ) trait IntoFloat {
18
21
type F ;
19
22
@@ -64,6 +67,64 @@ macro_rules! float_impls {
64
67
fraction. into_float_with_exponent( 0 ) - ( 1.0 - EPSILON / 2.0 )
65
68
}
66
69
}
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
+ }
67
128
}
68
129
}
69
130
float_impls ! { f32 , u32 , 23 , 127 , next_u32 }
0 commit comments