]> git.proxmox.com Git - cargo.git/blob - vendor/rand/src/distributions/float.rs
New upstream version 0.37.0
[cargo.git] / vendor / rand / src / distributions / float.rs
1 // Copyright 2018 Developers of the Rand project.
2 //
3 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5 // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6 // option. This file may not be copied, modified, or distributed
7 // except according to those terms.
8
9 //! Basic floating-point number distributions
10
11 use core::mem;
12 use crate::Rng;
13 use crate::distributions::{Distribution, Standard};
14 use crate::distributions::utils::FloatSIMDUtils;
15 #[cfg(feature="simd_support")]
16 use packed_simd::*;
17
18 /// A distribution to sample floating point numbers uniformly in the half-open
19 /// interval `(0, 1]`, i.e. including 1 but not 0.
20 ///
21 /// All values that can be generated are of the form `n * ε/2`. For `f32`
22 /// the 23 most significant random bits of a `u32` are used and for `f64` the
23 /// 53 most significant bits of a `u64` are used. The conversion uses the
24 /// multiplicative method.
25 ///
26 /// See also: [`Standard`] which samples from `[0, 1)`, [`Open01`]
27 /// which samples from `(0, 1)` and [`Uniform`] which samples from arbitrary
28 /// ranges.
29 ///
30 /// # Example
31 /// ```
32 /// use rand::{thread_rng, Rng};
33 /// use rand::distributions::OpenClosed01;
34 ///
35 /// let val: f32 = thread_rng().sample(OpenClosed01);
36 /// println!("f32 from (0, 1): {}", val);
37 /// ```
38 ///
39 /// [`Standard`]: crate::distributions::Standard
40 /// [`Open01`]: crate::distributions::Open01
41 /// [`Uniform`]: crate::distributions::uniform::Uniform
42 #[derive(Clone, Copy, Debug)]
43 pub struct OpenClosed01;
44
45 /// A distribution to sample floating point numbers uniformly in the open
46 /// interval `(0, 1)`, i.e. not including either endpoint.
47 ///
48 /// All values that can be generated are of the form `n * ε + ε/2`. For `f32`
49 /// the 22 most significant random bits of an `u32` are used, for `f64` 52 from
50 /// an `u64`. The conversion uses a transmute-based method.
51 ///
52 /// See also: [`Standard`] which samples from `[0, 1)`, [`OpenClosed01`]
53 /// which samples from `(0, 1]` and [`Uniform`] which samples from arbitrary
54 /// ranges.
55 ///
56 /// # Example
57 /// ```
58 /// use rand::{thread_rng, Rng};
59 /// use rand::distributions::Open01;
60 ///
61 /// let val: f32 = thread_rng().sample(Open01);
62 /// println!("f32 from (0, 1): {}", val);
63 /// ```
64 ///
65 /// [`Standard`]: crate::distributions::Standard
66 /// [`OpenClosed01`]: crate::distributions::OpenClosed01
67 /// [`Uniform`]: crate::distributions::uniform::Uniform
68 #[derive(Clone, Copy, Debug)]
69 pub struct Open01;
70
71
72 // This trait is needed by both this lib and rand_distr hence is a hidden export
73 #[doc(hidden)]
74 pub trait IntoFloat {
75 type F;
76
77 /// Helper method to combine the fraction and a contant exponent into a
78 /// float.
79 ///
80 /// Only the least significant bits of `self` may be set, 23 for `f32` and
81 /// 52 for `f64`.
82 /// The resulting value will fall in a range that depends on the exponent.
83 /// As an example the range with exponent 0 will be
84 /// [2<sup>0</sup>..2<sup>1</sup>), which is [1..2).
85 fn into_float_with_exponent(self, exponent: i32) -> Self::F;
86 }
87
88 macro_rules! float_impls {
89 ($ty:ident, $uty:ident, $f_scalar:ident, $u_scalar:ty,
90 $fraction_bits:expr, $exponent_bias:expr) => {
91 impl IntoFloat for $uty {
92 type F = $ty;
93 #[inline(always)]
94 fn into_float_with_exponent(self, exponent: i32) -> $ty {
95 // The exponent is encoded using an offset-binary representation
96 let exponent_bits: $u_scalar =
97 (($exponent_bias + exponent) as $u_scalar) << $fraction_bits;
98 // TODO: use from_bits when min compiler > 1.25 (see #545)
99 // $ty::from_bits(self | exponent_bits)
100 unsafe{ mem::transmute(self | exponent_bits) }
101 }
102 }
103
104 impl Distribution<$ty> for Standard {
105 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $ty {
106 // Multiply-based method; 24/53 random bits; [0, 1) interval.
107 // We use the most significant bits because for simple RNGs
108 // those are usually more random.
109 let float_size = mem::size_of::<$f_scalar>() as u32 * 8;
110 let precision = $fraction_bits + 1;
111 let scale = 1.0 / ((1 as $u_scalar << precision) as $f_scalar);
112
113 let value: $uty = rng.gen();
114 let value = value >> (float_size - precision);
115 scale * $ty::cast_from_int(value)
116 }
117 }
118
119 impl Distribution<$ty> for OpenClosed01 {
120 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $ty {
121 // Multiply-based method; 24/53 random bits; (0, 1] interval.
122 // We use the most significant bits because for simple RNGs
123 // those are usually more random.
124 let float_size = mem::size_of::<$f_scalar>() as u32 * 8;
125 let precision = $fraction_bits + 1;
126 let scale = 1.0 / ((1 as $u_scalar << precision) as $f_scalar);
127
128 let value: $uty = rng.gen();
129 let value = value >> (float_size - precision);
130 // Add 1 to shift up; will not overflow because of right-shift:
131 scale * $ty::cast_from_int(value + 1)
132 }
133 }
134
135 impl Distribution<$ty> for Open01 {
136 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $ty {
137 // Transmute-based method; 23/52 random bits; (0, 1) interval.
138 // We use the most significant bits because for simple RNGs
139 // those are usually more random.
140 use core::$f_scalar::EPSILON;
141 let float_size = mem::size_of::<$f_scalar>() as u32 * 8;
142
143 let value: $uty = rng.gen();
144 let fraction = value >> (float_size - $fraction_bits);
145 fraction.into_float_with_exponent(0) - (1.0 - EPSILON / 2.0)
146 }
147 }
148 }
149 }
150
151 float_impls! { f32, u32, f32, u32, 23, 127 }
152 float_impls! { f64, u64, f64, u64, 52, 1023 }
153
154 #[cfg(feature="simd_support")]
155 float_impls! { f32x2, u32x2, f32, u32, 23, 127 }
156 #[cfg(feature="simd_support")]
157 float_impls! { f32x4, u32x4, f32, u32, 23, 127 }
158 #[cfg(feature="simd_support")]
159 float_impls! { f32x8, u32x8, f32, u32, 23, 127 }
160 #[cfg(feature="simd_support")]
161 float_impls! { f32x16, u32x16, f32, u32, 23, 127 }
162
163 #[cfg(feature="simd_support")]
164 float_impls! { f64x2, u64x2, f64, u64, 52, 1023 }
165 #[cfg(feature="simd_support")]
166 float_impls! { f64x4, u64x4, f64, u64, 52, 1023 }
167 #[cfg(feature="simd_support")]
168 float_impls! { f64x8, u64x8, f64, u64, 52, 1023 }
169
170
171 #[cfg(test)]
172 mod tests {
173 use crate::Rng;
174 use crate::distributions::{Open01, OpenClosed01};
175 use crate::rngs::mock::StepRng;
176 #[cfg(feature="simd_support")]
177 use packed_simd::*;
178
179 const EPSILON32: f32 = ::core::f32::EPSILON;
180 const EPSILON64: f64 = ::core::f64::EPSILON;
181
182 macro_rules! test_f32 {
183 ($fnn:ident, $ty:ident, $ZERO:expr, $EPSILON:expr) => {
184 #[test]
185 fn $fnn() {
186 // Standard
187 let mut zeros = StepRng::new(0, 0);
188 assert_eq!(zeros.gen::<$ty>(), $ZERO);
189 let mut one = StepRng::new(1 << 8 | 1 << (8 + 32), 0);
190 assert_eq!(one.gen::<$ty>(), $EPSILON / 2.0);
191 let mut max = StepRng::new(!0, 0);
192 assert_eq!(max.gen::<$ty>(), 1.0 - $EPSILON / 2.0);
193
194 // OpenClosed01
195 let mut zeros = StepRng::new(0, 0);
196 assert_eq!(zeros.sample::<$ty, _>(OpenClosed01),
197 0.0 + $EPSILON / 2.0);
198 let mut one = StepRng::new(1 << 8 | 1 << (8 + 32), 0);
199 assert_eq!(one.sample::<$ty, _>(OpenClosed01), $EPSILON);
200 let mut max = StepRng::new(!0, 0);
201 assert_eq!(max.sample::<$ty, _>(OpenClosed01), $ZERO + 1.0);
202
203 // Open01
204 let mut zeros = StepRng::new(0, 0);
205 assert_eq!(zeros.sample::<$ty, _>(Open01), 0.0 + $EPSILON / 2.0);
206 let mut one = StepRng::new(1 << 9 | 1 << (9 + 32), 0);
207 assert_eq!(one.sample::<$ty, _>(Open01), $EPSILON / 2.0 * 3.0);
208 let mut max = StepRng::new(!0, 0);
209 assert_eq!(max.sample::<$ty, _>(Open01), 1.0 - $EPSILON / 2.0);
210 }
211 }
212 }
213 test_f32! { f32_edge_cases, f32, 0.0, EPSILON32 }
214 #[cfg(feature="simd_support")]
215 test_f32! { f32x2_edge_cases, f32x2, f32x2::splat(0.0), f32x2::splat(EPSILON32) }
216 #[cfg(feature="simd_support")]
217 test_f32! { f32x4_edge_cases, f32x4, f32x4::splat(0.0), f32x4::splat(EPSILON32) }
218 #[cfg(feature="simd_support")]
219 test_f32! { f32x8_edge_cases, f32x8, f32x8::splat(0.0), f32x8::splat(EPSILON32) }
220 #[cfg(feature="simd_support")]
221 test_f32! { f32x16_edge_cases, f32x16, f32x16::splat(0.0), f32x16::splat(EPSILON32) }
222
223 macro_rules! test_f64 {
224 ($fnn:ident, $ty:ident, $ZERO:expr, $EPSILON:expr) => {
225 #[test]
226 fn $fnn() {
227 // Standard
228 let mut zeros = StepRng::new(0, 0);
229 assert_eq!(zeros.gen::<$ty>(), $ZERO);
230 let mut one = StepRng::new(1 << 11, 0);
231 assert_eq!(one.gen::<$ty>(), $EPSILON / 2.0);
232 let mut max = StepRng::new(!0, 0);
233 assert_eq!(max.gen::<$ty>(), 1.0 - $EPSILON / 2.0);
234
235 // OpenClosed01
236 let mut zeros = StepRng::new(0, 0);
237 assert_eq!(zeros.sample::<$ty, _>(OpenClosed01),
238 0.0 + $EPSILON / 2.0);
239 let mut one = StepRng::new(1 << 11, 0);
240 assert_eq!(one.sample::<$ty, _>(OpenClosed01), $EPSILON);
241 let mut max = StepRng::new(!0, 0);
242 assert_eq!(max.sample::<$ty, _>(OpenClosed01), $ZERO + 1.0);
243
244 // Open01
245 let mut zeros = StepRng::new(0, 0);
246 assert_eq!(zeros.sample::<$ty, _>(Open01), 0.0 + $EPSILON / 2.0);
247 let mut one = StepRng::new(1 << 12, 0);
248 assert_eq!(one.sample::<$ty, _>(Open01), $EPSILON / 2.0 * 3.0);
249 let mut max = StepRng::new(!0, 0);
250 assert_eq!(max.sample::<$ty, _>(Open01), 1.0 - $EPSILON / 2.0);
251 }
252 }
253 }
254 test_f64! { f64_edge_cases, f64, 0.0, EPSILON64 }
255 #[cfg(feature="simd_support")]
256 test_f64! { f64x2_edge_cases, f64x2, f64x2::splat(0.0), f64x2::splat(EPSILON64) }
257 #[cfg(feature="simd_support")]
258 test_f64! { f64x4_edge_cases, f64x4, f64x4::splat(0.0), f64x4::splat(EPSILON64) }
259 #[cfg(feature="simd_support")]
260 test_f64! { f64x8_edge_cases, f64x8, f64x8::splat(0.0), f64x8::splat(EPSILON64) }
261 }