|
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