|
9 | 9 | // except according to those terms.
|
10 | 10 |
|
11 | 11 | //! 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 | +} |
54 | 30 |
|
55 | 31 | 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) } |
83 | 42 | }
|
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) |
91 | 64 | }
|
92 | 65 | }
|
93 | 66 | }
|
94 | 67 | }
|
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 } |
97 | 70 |
|
98 | 71 |
|
99 | 72 | #[cfg(test)]
|
100 | 73 | mod tests {
|
101 | 74 | use Rng;
|
102 | 75 | use mock::StepRng;
|
103 |
| - use distributions::{Open01, Closed01}; |
104 | 76 |
|
105 | 77 | const EPSILON32: f32 = ::core::f32::EPSILON;
|
106 | 78 | const EPSILON64: f64 = ::core::f64::EPSILON;
|
107 | 79 |
|
108 | 80 | #[test]
|
109 | 81 | fn floating_point_edge_cases() {
|
110 | 82 | 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); |
122 | 85 |
|
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>(); |
149 | 88 | 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); |
166 | 89 |
|
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); |
179 | 93 |
|
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); |
183 | 97 | }
|
184 | 98 | }
|
0 commit comments