|
13 | 13 | use core::mem;
|
14 | 14 | use Rng;
|
15 | 15 | use distributions::{Distribution, Standard};
|
| 16 | +#[cfg(feature="simd_support")] |
| 17 | +use core::simd::*; |
16 | 18 |
|
17 | 19 | /// A distribution to sample floating point numbers uniformly in the half-open
|
18 | 20 | /// interval `(0, 1]`, i.e. including 1 but not 0.
|
@@ -144,63 +146,173 @@ float_impls! { f32, u32, 23, 127 }
|
144 | 146 | float_impls! { f64, u64, 52, 1023 }
|
145 | 147 |
|
146 | 148 |
|
| 149 | +#[cfg(feature="simd_support")] |
| 150 | +macro_rules! simd_float_impls { |
| 151 | + ($ty:ident, $uty:ident, $f_scalar:ty, $u_scalar:ty, |
| 152 | + $fraction_bits:expr, $exponent_bias:expr) => { |
| 153 | + impl IntoFloat for $uty { |
| 154 | + type F = $ty; |
| 155 | + #[inline(always)] |
| 156 | + fn into_float_with_exponent(self, exponent: i32) -> $ty { |
| 157 | + // The exponent is encoded using an offset-binary representation |
| 158 | + let exponent_bits: $u_scalar = |
| 159 | + (($exponent_bias + exponent) as $u_scalar) << $fraction_bits; |
| 160 | + unsafe { mem::transmute(self | $uty::splat(exponent_bits)) } |
| 161 | + } |
| 162 | + } |
| 163 | + |
| 164 | + impl Distribution<$ty> for Standard { |
| 165 | + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $ty { |
| 166 | + // Multiply-based method; 24/53 random bits; [0, 1) interval. |
| 167 | + // We use the most significant bits because for simple RNGs |
| 168 | + // those are usually more random. |
| 169 | + let float_size = mem::size_of::<$f_scalar>() * 8; |
| 170 | + let precision = $fraction_bits + 1; |
| 171 | + let scale = $ty::splat(1.0 / ((1 as $u_scalar << precision) as $f_scalar)); |
| 172 | + |
| 173 | + let value: $uty = rng.gen(); |
| 174 | + let value = $ty::from(value >> (float_size - precision)); |
| 175 | + scale * value |
| 176 | + } |
| 177 | + } |
| 178 | + |
| 179 | + impl Distribution<$ty> for OpenClosed01 { |
| 180 | + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $ty { |
| 181 | + // Multiply-based method; 24/53 random bits; (0, 1] interval. |
| 182 | + // We use the most significant bits because for simple RNGs |
| 183 | + // those are usually more random. |
| 184 | + let float_size = mem::size_of::<$f_scalar>() * 8; |
| 185 | + let precision = $fraction_bits + 1; |
| 186 | + let scale = $ty::splat(1.0 / ((1 as $u_scalar << precision) as $f_scalar)); |
| 187 | + |
| 188 | + let value: $uty = rng.gen(); |
| 189 | + // Add 1 to shift up; will not overflow because of right-shift: |
| 190 | + let value = $ty::from((value >> (float_size - precision)) + 1); |
| 191 | + scale * value |
| 192 | + } |
| 193 | + } |
| 194 | + |
| 195 | + impl Distribution<$ty> for Open01 { |
| 196 | + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $ty { |
| 197 | + // Transmute-based method; 23/52 random bits; (0, 1) interval. |
| 198 | + // We use the most significant bits because for simple RNGs |
| 199 | + // those are usually more random. |
| 200 | + const EPSILON: $f_scalar = 1.0 / (1u64 << $fraction_bits) as $f_scalar; |
| 201 | + let float_size = mem::size_of::<$f_scalar>() * 8; |
| 202 | + |
| 203 | + let value: $uty = rng.gen(); |
| 204 | + let fraction = value >> (float_size - $fraction_bits); |
| 205 | + fraction.into_float_with_exponent(0) - $ty::splat(1.0 - EPSILON / 2.0) |
| 206 | + } |
| 207 | + } |
| 208 | + } |
| 209 | +} |
| 210 | + |
| 211 | +#[cfg(feature="simd_support")] |
| 212 | +simd_float_impls! { f32x2, u32x2, f32, u32, 23, 127 } |
| 213 | +#[cfg(feature="simd_support")] |
| 214 | +simd_float_impls! { f32x4, u32x4, f32, u32, 23, 127 } |
| 215 | +#[cfg(feature="simd_support")] |
| 216 | +simd_float_impls! { f32x8, u32x8, f32, u32, 23, 127 } |
| 217 | +#[cfg(feature="simd_support")] |
| 218 | +simd_float_impls! { f32x16, u32x16, f32, u32, 23, 127 } |
| 219 | + |
| 220 | +#[cfg(feature="simd_support")] |
| 221 | +simd_float_impls! { f64x2, u64x2, f64, u64, 52, 1023 } |
| 222 | +#[cfg(feature="simd_support")] |
| 223 | +simd_float_impls! { f64x4, u64x4, f64, u64, 52, 1023 } |
| 224 | +#[cfg(feature="simd_support")] |
| 225 | +simd_float_impls! { f64x8, u64x8, f64, u64, 52, 1023 } |
| 226 | + |
| 227 | + |
147 | 228 | #[cfg(test)]
|
148 | 229 | mod tests {
|
149 | 230 | use Rng;
|
150 | 231 | use distributions::{Open01, OpenClosed01};
|
151 | 232 | use rngs::mock::StepRng;
|
| 233 | + #[cfg(feature="simd_support")] |
| 234 | + use core::simd::*; |
152 | 235 |
|
153 | 236 | const EPSILON32: f32 = ::core::f32::EPSILON;
|
154 | 237 | const EPSILON64: f64 = ::core::f64::EPSILON;
|
155 | 238 |
|
156 |
| - #[test] |
157 |
| - fn standard_fp_edge_cases() { |
158 |
| - let mut zeros = StepRng::new(0, 0); |
159 |
| - assert_eq!(zeros.gen::<f32>(), 0.0); |
160 |
| - assert_eq!(zeros.gen::<f64>(), 0.0); |
161 |
| - |
162 |
| - let mut one32 = StepRng::new(1 << 8, 0); |
163 |
| - assert_eq!(one32.gen::<f32>(), EPSILON32 / 2.0); |
164 |
| - |
165 |
| - let mut one64 = StepRng::new(1 << 11, 0); |
166 |
| - assert_eq!(one64.gen::<f64>(), EPSILON64 / 2.0); |
167 |
| - |
168 |
| - let mut max = StepRng::new(!0, 0); |
169 |
| - assert_eq!(max.gen::<f32>(), 1.0 - EPSILON32 / 2.0); |
170 |
| - assert_eq!(max.gen::<f64>(), 1.0 - EPSILON64 / 2.0); |
171 |
| - } |
172 |
| - |
173 |
| - #[test] |
174 |
| - fn openclosed01_edge_cases() { |
175 |
| - let mut zeros = StepRng::new(0, 0); |
176 |
| - assert_eq!(zeros.sample::<f32, _>(OpenClosed01), 0.0 + EPSILON32 / 2.0); |
177 |
| - assert_eq!(zeros.sample::<f64, _>(OpenClosed01), 0.0 + EPSILON64 / 2.0); |
178 |
| - |
179 |
| - let mut one32 = StepRng::new(1 << 8, 0); |
180 |
| - assert_eq!(one32.sample::<f32, _>(OpenClosed01), EPSILON32); |
| 239 | + macro_rules! test_f32 { |
| 240 | + ($fnn:ident, $ty:ident, $ZERO:expr, $EPSILON:expr) => { |
| 241 | + #[test] |
| 242 | + fn $fnn() { |
| 243 | + // Standard |
| 244 | + let mut zeros = StepRng::new(0, 0); |
| 245 | + assert_eq!(zeros.gen::<$ty>(), $ZERO); |
| 246 | + let mut one = StepRng::new(1 << 8 | 1 << (8 + 32), 0); |
| 247 | + assert_eq!(one.gen::<$ty>(), $EPSILON / 2.0); |
| 248 | + let mut max = StepRng::new(!0, 0); |
| 249 | + assert_eq!(max.gen::<$ty>(), 1.0 - $EPSILON / 2.0); |
181 | 250 |
|
182 |
| - let mut one64 = StepRng::new(1 << 11, 0); |
183 |
| - assert_eq!(one64.sample::<f64, _>(OpenClosed01), EPSILON64); |
| 251 | + // OpenClosed01 |
| 252 | + let mut zeros = StepRng::new(0, 0); |
| 253 | + assert_eq!(zeros.sample::<$ty, _>(OpenClosed01), |
| 254 | + 0.0 + $EPSILON / 2.0); |
| 255 | + let mut one = StepRng::new(1 << 8 | 1 << (8 + 32), 0); |
| 256 | + assert_eq!(one.sample::<$ty, _>(OpenClosed01), $EPSILON); |
| 257 | + let mut max = StepRng::new(!0, 0); |
| 258 | + assert_eq!(max.sample::<$ty, _>(OpenClosed01), $ZERO + 1.0); |
184 | 259 |
|
185 |
| - let mut max = StepRng::new(!0, 0); |
186 |
| - assert_eq!(max.sample::<f32, _>(OpenClosed01), 1.0); |
187 |
| - assert_eq!(max.sample::<f64, _>(OpenClosed01), 1.0); |
| 260 | + // Open01 |
| 261 | + let mut zeros = StepRng::new(0, 0); |
| 262 | + assert_eq!(zeros.sample::<$ty, _>(Open01), 0.0 + $EPSILON / 2.0); |
| 263 | + let mut one = StepRng::new(1 << 9 | 1 << (9 + 32), 0); |
| 264 | + assert_eq!(one.sample::<$ty, _>(Open01), $EPSILON / 2.0 * 3.0); |
| 265 | + let mut max = StepRng::new(!0, 0); |
| 266 | + assert_eq!(max.sample::<$ty, _>(Open01), 1.0 - $EPSILON / 2.0); |
| 267 | + } |
| 268 | + } |
188 | 269 | }
|
| 270 | + test_f32! { f32_edge_cases, f32, 0.0, ::core::f32::EPSILON } |
| 271 | + #[cfg(feature="simd_support")] |
| 272 | + test_f32! { f32x2_edge_cases, f32x2, f32x2::splat(0.0), f32x2::splat(EPSILON32) } |
| 273 | + #[cfg(feature="simd_support")] |
| 274 | + test_f32! { f32x4_edge_cases, f32x4, f32x4::splat(0.0), f32x4::splat(EPSILON32) } |
| 275 | + #[cfg(feature="simd_support")] |
| 276 | + test_f32! { f32x8_edge_cases, f32x8, f32x8::splat(0.0), f32x8::splat(EPSILON32) } |
| 277 | + #[cfg(feature="simd_support")] |
| 278 | + test_f32! { f32x16_edge_cases, f32x16, f32x16::splat(0.0), f32x16::splat(EPSILON32) } |
189 | 279 |
|
190 |
| - #[test] |
191 |
| - fn open01_edge_cases() { |
192 |
| - let mut zeros = StepRng::new(0, 0); |
193 |
| - assert_eq!(zeros.sample::<f32, _>(Open01), 0.0 + EPSILON32 / 2.0); |
194 |
| - assert_eq!(zeros.sample::<f64, _>(Open01), 0.0 + EPSILON64 / 2.0); |
| 280 | + macro_rules! test_f64 { |
| 281 | + ($fnn:ident, $ty:ident, $ZERO:expr, $EPSILON:expr) => { |
| 282 | + #[test] |
| 283 | + fn $fnn() { |
| 284 | + // Standard |
| 285 | + let mut zeros = StepRng::new(0, 0); |
| 286 | + assert_eq!(zeros.gen::<$ty>(), $ZERO); |
| 287 | + let mut one = StepRng::new(1 << 11, 0); |
| 288 | + assert_eq!(one.gen::<$ty>(), $EPSILON / 2.0); |
| 289 | + let mut max = StepRng::new(!0, 0); |
| 290 | + assert_eq!(max.gen::<$ty>(), 1.0 - $EPSILON / 2.0); |
195 | 291 |
|
196 |
| - let mut one32 = StepRng::new(1 << 9, 0); |
197 |
| - assert_eq!(one32.sample::<f32, _>(Open01), EPSILON32 / 2.0 * 3.0); |
| 292 | + // OpenClosed01 |
| 293 | + let mut zeros = StepRng::new(0, 0); |
| 294 | + assert_eq!(zeros.sample::<$ty, _>(OpenClosed01), |
| 295 | + 0.0 + $EPSILON / 2.0); |
| 296 | + let mut one = StepRng::new(1 << 11, 0); |
| 297 | + assert_eq!(one.sample::<$ty, _>(OpenClosed01), $EPSILON); |
| 298 | + let mut max = StepRng::new(!0, 0); |
| 299 | + assert_eq!(max.sample::<$ty, _>(OpenClosed01), $ZERO + 1.0); |
198 | 300 |
|
199 |
| - let mut one64 = StepRng::new(1 << 12, 0); |
200 |
| - assert_eq!(one64.sample::<f64, _>(Open01), EPSILON64 / 2.0 * 3.0); |
201 |
| - |
202 |
| - let mut max = StepRng::new(!0, 0); |
203 |
| - assert_eq!(max.sample::<f32, _>(Open01), 1.0 - EPSILON32 / 2.0); |
204 |
| - assert_eq!(max.sample::<f64, _>(Open01), 1.0 - EPSILON64 / 2.0); |
| 301 | + // Open01 |
| 302 | + let mut zeros = StepRng::new(0, 0); |
| 303 | + assert_eq!(zeros.sample::<$ty, _>(Open01), 0.0 + $EPSILON / 2.0); |
| 304 | + let mut one = StepRng::new(1 << 12, 0); |
| 305 | + assert_eq!(one.sample::<$ty, _>(Open01), $EPSILON / 2.0 * 3.0); |
| 306 | + let mut max = StepRng::new(!0, 0); |
| 307 | + assert_eq!(max.sample::<$ty, _>(Open01), 1.0 - $EPSILON / 2.0); |
| 308 | + } |
| 309 | + } |
205 | 310 | }
|
| 311 | + test_f64! { f64_edge_cases, f64, 0.0, EPSILON64 } |
| 312 | + #[cfg(feature="simd_support")] |
| 313 | + test_f64! { f64x2_edge_cases, f64x2, f64x2::splat(0.0), f64x2::splat(EPSILON64) } |
| 314 | + #[cfg(feature="simd_support")] |
| 315 | + test_f64! { f64x4_edge_cases, f64x4, f64x4::splat(0.0), f64x4::splat(EPSILON64) } |
| 316 | + #[cfg(feature="simd_support")] |
| 317 | + test_f64! { f64x8_edge_cases, f64x8, f64x8::splat(0.0), f64x8::splat(EPSILON64) } |
206 | 318 | }
|
0 commit comments