1use super::{Error, SampleBorrow, SampleUniform, UniformSampler};
13use crate::distr::float::IntoFloat;
14use crate::distr::utils::{BoolAsSIMD, FloatAsSIMD, FloatSIMDUtils, IntAsSIMD};
15use crate::{Rng, RngExt};
16
17#[cfg(feature = "simd_support")]
18use core::simd::prelude::*;
19
20#[cfg(feature = "serde")]
21use serde::{Deserialize, Serialize};
22
23#[derive(Clone, Copy, Debug, PartialEq)]
47#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
48pub struct UniformFloat<X> {
49 low: X,
50 scale: X,
51}
52
53macro_rules! uniform_float_impl {
54 ($($meta:meta)?, $ty:ty, $uty:ident, $f_scalar:ident, $u_scalar:ident, $bits_to_discard:expr) => {
55 $(#[cfg($meta)])?
56 impl UniformFloat<$ty> {
57 fn new_bounded(low: $ty, high: $ty, mut scale: $ty) -> Self {
69 let max_rand = <$ty>::splat(1.0 as $f_scalar - $f_scalar::EPSILON);
70
71 loop {
72 let mask = (scale * max_rand + low).le_mask(high);
73 if mask.all() {
74 break;
75 }
76 scale = scale.decrease_masked(!mask);
77 }
78
79 debug_assert!(<$ty>::splat(0.0).all_le(scale));
80
81 UniformFloat { low, scale }
82 }
83 }
84
85 $(#[cfg($meta)])?
86 impl SampleUniform for $ty {
87 type Sampler = UniformFloat<$ty>;
88 }
89
90 $(#[cfg($meta)])?
91 impl UniformSampler for UniformFloat<$ty> {
92 type X = $ty;
93
94 fn new<B1, B2>(low_b: B1, high_b: B2) -> Result<Self, Error>
95 where
96 B1: SampleBorrow<Self::X> + Sized,
97 B2: SampleBorrow<Self::X> + Sized,
98 {
99 let low = *low_b.borrow();
100 let high = *high_b.borrow();
101 #[cfg(debug_assertions)]
102 if !(low.all_finite()) || !(high.all_finite()) {
103 return Err(Error::NonFinite);
104 }
105 if !(low.all_lt(high)) {
106 return Err(Error::EmptyRange);
107 }
108
109 let scale = high - low;
110 if !(scale.all_finite()) {
111 return Err(Error::NonFinite);
112 }
113
114 Ok(Self::new_bounded(low, high, scale))
115 }
116
117 fn new_inclusive<B1, B2>(low_b: B1, high_b: B2) -> Result<Self, Error>
118 where
119 B1: SampleBorrow<Self::X> + Sized,
120 B2: SampleBorrow<Self::X> + Sized,
121 {
122 let low = *low_b.borrow();
123 let high = *high_b.borrow();
124 #[cfg(debug_assertions)]
125 if !(low.all_finite()) || !(high.all_finite()) {
126 return Err(Error::NonFinite);
127 }
128 if !low.all_le(high) {
129 return Err(Error::EmptyRange);
130 }
131
132 let range = high - low;
133 if !range.all_finite() {
134 return Err(Error::NonFinite);
135 }
136
137 let max_rand = <$ty>::splat(1.0 as $f_scalar - $f_scalar::EPSILON);
138 let scale = range / max_rand;
139
140 Ok(Self::new_bounded(low, high, scale))
141 }
142
143 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Self::X {
144 let value1_2 = (rng.random::<$uty>() >> $uty::splat($bits_to_discard)).into_float_with_exponent(0);
146
147 let value0_1 = value1_2 - <$ty>::splat(1.0);
149
150 value0_1 * self.scale + self.low
156 }
157
158 #[inline]
159 fn sample_single<R: Rng + ?Sized, B1, B2>(low_b: B1, high_b: B2, rng: &mut R) -> Result<Self::X, Error>
160 where
161 B1: SampleBorrow<Self::X> + Sized,
162 B2: SampleBorrow<Self::X> + Sized,
163 {
164 Self::sample_single_inclusive(low_b, high_b, rng)
165 }
166
167 #[inline]
168 fn sample_single_inclusive<R: Rng + ?Sized, B1, B2>(low_b: B1, high_b: B2, rng: &mut R) -> Result<Self::X, Error>
169 where
170 B1: SampleBorrow<Self::X> + Sized,
171 B2: SampleBorrow<Self::X> + Sized,
172 {
173 let low = *low_b.borrow();
174 let high = *high_b.borrow();
175 #[cfg(debug_assertions)]
176 if !low.all_finite() || !high.all_finite() {
177 return Err(Error::NonFinite);
178 }
179 if !low.all_le(high) {
180 return Err(Error::EmptyRange);
181 }
182 let scale = high - low;
183 if !scale.all_finite() {
184 return Err(Error::NonFinite);
185 }
186
187 let value1_2 =
189 (rng.random::<$uty>() >> $uty::splat($bits_to_discard)).into_float_with_exponent(0);
190
191 let value0_1 = value1_2 - <$ty>::splat(1.0);
193
194 Ok(value0_1 * scale + low)
197 }
198 }
199 };
200}
201
202uniform_float_impl! { , f32, u32, f32, u32, 32 - 23 }
203uniform_float_impl! { , f64, u64, f64, u64, 64 - 52 }
204
205#[cfg(feature = "simd_support")]
206uniform_float_impl! { feature = "simd_support", f32x2, u32x2, f32, u32, 32 - 23 }
207#[cfg(feature = "simd_support")]
208uniform_float_impl! { feature = "simd_support", f32x4, u32x4, f32, u32, 32 - 23 }
209#[cfg(feature = "simd_support")]
210uniform_float_impl! { feature = "simd_support", f32x8, u32x8, f32, u32, 32 - 23 }
211#[cfg(feature = "simd_support")]
212uniform_float_impl! { feature = "simd_support", f32x16, u32x16, f32, u32, 32 - 23 }
213
214#[cfg(feature = "simd_support")]
215uniform_float_impl! { feature = "simd_support", f64x2, u64x2, f64, u64, 64 - 52 }
216#[cfg(feature = "simd_support")]
217uniform_float_impl! { feature = "simd_support", f64x4, u64x4, f64, u64, 64 - 52 }
218#[cfg(feature = "simd_support")]
219uniform_float_impl! { feature = "simd_support", f64x8, u64x8, f64, u64, 64 - 52 }
220
221#[cfg(test)]
222mod tests {
223 use super::*;
224 use crate::distr::{Uniform, utils::FloatSIMDScalarUtils};
225 use crate::test::{const_rng, step_rng};
226
227 #[test]
228 #[cfg_attr(miri, ignore)] fn test_floats() {
230 let mut rng = crate::test::rng(252);
231 let mut zero_rng = const_rng(0);
232 let mut max_rng = const_rng(0xffff_ffff_ffff_ffff);
233 macro_rules! t {
234 ($ty:ty, $f_scalar:ident, $bits_shifted:expr) => {{
235 let v: &[($f_scalar, $f_scalar)] = &[
236 (0.0, 100.0),
237 (-1e35, -1e25),
238 (1e-35, 1e-25),
239 (-1e35, 1e35),
240 (<$f_scalar>::from_bits(0), <$f_scalar>::from_bits(3)),
241 (-<$f_scalar>::from_bits(10), -<$f_scalar>::from_bits(1)),
242 (-<$f_scalar>::from_bits(5), 0.0),
243 (-<$f_scalar>::from_bits(7), -0.0),
244 (0.1 * $f_scalar::MAX, $f_scalar::MAX),
245 (-$f_scalar::MAX * 0.2, $f_scalar::MAX * 0.7),
246 (0.0, $f_scalar::MAX),
247 (-$f_scalar::MAX, 0.0),
248 ];
249 for &(low_scalar, high_scalar) in v.iter() {
250 for lane in 0..<$ty>::LEN {
251 let low = <$ty>::splat(0.0 as $f_scalar).replace(lane, low_scalar);
252 let high = <$ty>::splat(1.0 as $f_scalar).replace(lane, high_scalar);
253 let my_uniform = Uniform::new(low, high).unwrap();
254 let my_incl_uniform = Uniform::new_inclusive(low, high).unwrap();
255 for _ in 0..100 {
256 let v = rng.sample(my_uniform).extract_lane(lane);
257 assert!(low_scalar <= v && v <= high_scalar);
258 let v = rng.sample(my_incl_uniform).extract_lane(lane);
259 assert!(low_scalar <= v && v <= high_scalar);
260 let v =
261 <$ty as SampleUniform>::Sampler::sample_single(low, high, &mut rng)
262 .unwrap()
263 .extract_lane(lane);
264 assert!(low_scalar <= v && v <= high_scalar);
265 let v = <$ty as SampleUniform>::Sampler::sample_single_inclusive(
266 low, high, &mut rng,
267 )
268 .unwrap()
269 .extract_lane(lane);
270 assert!(low_scalar <= v && v <= high_scalar);
271 }
272
273 assert_eq!(
274 rng.sample(Uniform::new_inclusive(low, low).unwrap())
275 .extract_lane(lane),
276 low_scalar
277 );
278
279 assert_eq!(zero_rng.sample(my_uniform).extract_lane(lane), low_scalar);
280 assert_eq!(
281 zero_rng.sample(my_incl_uniform).extract_lane(lane),
282 low_scalar
283 );
284 assert_eq!(
285 <$ty as SampleUniform>::Sampler::sample_single(
286 low,
287 high,
288 &mut zero_rng
289 )
290 .unwrap()
291 .extract_lane(lane),
292 low_scalar
293 );
294 assert_eq!(
295 <$ty as SampleUniform>::Sampler::sample_single_inclusive(
296 low,
297 high,
298 &mut zero_rng
299 )
300 .unwrap()
301 .extract_lane(lane),
302 low_scalar
303 );
304
305 assert!(max_rng.sample(my_uniform).extract_lane(lane) <= high_scalar);
306 assert!(max_rng.sample(my_incl_uniform).extract_lane(lane) <= high_scalar);
307 assert!(
312 <$ty as SampleUniform>::Sampler::sample_single_inclusive(
313 low,
314 high,
315 &mut max_rng
316 )
317 .unwrap()
318 .extract_lane(lane)
319 <= high_scalar
320 );
321
322 if (high_scalar - low_scalar) > 0.0001 {
326 let mut lowering_max_rng =
327 step_rng(0xffff_ffff_ffff_ffff, (-1i64 << $bits_shifted) as u64);
328 assert!(
329 <$ty as SampleUniform>::Sampler::sample_single(
330 low,
331 high,
332 &mut lowering_max_rng
333 )
334 .unwrap()
335 .extract_lane(lane)
336 <= high_scalar
337 );
338 }
339 }
340 }
341
342 assert_eq!(
343 rng.sample(Uniform::new_inclusive($f_scalar::MAX, $f_scalar::MAX).unwrap()),
344 $f_scalar::MAX
345 );
346 assert_eq!(
347 rng.sample(Uniform::new_inclusive(-$f_scalar::MAX, -$f_scalar::MAX).unwrap()),
348 -$f_scalar::MAX
349 );
350 }};
351 }
352
353 t!(f32, f32, 32 - 23);
354 t!(f64, f64, 64 - 52);
355 #[cfg(feature = "simd_support")]
356 {
357 t!(f32x2, f32, 32 - 23);
358 t!(f32x4, f32, 32 - 23);
359 t!(f32x8, f32, 32 - 23);
360 t!(f32x16, f32, 32 - 23);
361 t!(f64x2, f64, 64 - 52);
362 t!(f64x4, f64, 64 - 52);
363 t!(f64x8, f64, 64 - 52);
364 }
365 }
366
367 #[test]
368 fn test_float_overflow() {
369 assert_eq!(Uniform::try_from(f64::MIN..f64::MAX), Err(Error::NonFinite));
370 assert_eq!(
371 Uniform::try_from(f64::MIN..=f64::MAX),
372 Err(Error::NonFinite)
373 );
374 }
375
376 #[test]
377 #[should_panic]
378 fn test_float_overflow_single() {
379 let mut rng = crate::test::rng(252);
380 rng.random_range(f64::MIN..f64::MAX);
381 }
382
383 #[test]
384 #[cfg(all(feature = "std", panic = "unwind"))]
385 fn test_float_assertions() {
386 use super::SampleUniform;
387 fn range<T: SampleUniform>(low: T, high: T) -> Result<T, Error> {
388 let mut rng = crate::test::rng(253);
389 T::Sampler::sample_single(low, high, &mut rng)
390 }
391
392 macro_rules! t {
393 ($ty:ident, $f_scalar:ident) => {{
394 let v: &[($f_scalar, $f_scalar)] = &[
395 ($f_scalar::NAN, 0.0),
396 (1.0, $f_scalar::NAN),
397 ($f_scalar::NAN, $f_scalar::NAN),
398 (1.0, 0.5),
399 ($f_scalar::MAX, -$f_scalar::MAX),
400 ($f_scalar::INFINITY, $f_scalar::INFINITY),
401 ($f_scalar::NEG_INFINITY, $f_scalar::NEG_INFINITY),
402 ($f_scalar::NEG_INFINITY, 5.0),
403 (5.0, $f_scalar::INFINITY),
404 ($f_scalar::NAN, $f_scalar::INFINITY),
405 ($f_scalar::NEG_INFINITY, $f_scalar::NAN),
406 ($f_scalar::NEG_INFINITY, $f_scalar::INFINITY),
407 ];
408 for &(low_scalar, high_scalar) in v.iter() {
409 for lane in 0..<$ty>::LEN {
410 let low = <$ty>::splat(0.0 as $f_scalar).replace(lane, low_scalar);
411 let high = <$ty>::splat(1.0 as $f_scalar).replace(lane, high_scalar);
412 assert!(range(low, high).is_err());
413 assert!(Uniform::new(low, high).is_err());
414 assert!(Uniform::new_inclusive(low, high).is_err());
415 assert!(Uniform::new(low, low).is_err());
416 }
417 }
418 }};
419 }
420
421 t!(f32, f32);
422 t!(f64, f64);
423 #[cfg(feature = "simd_support")]
424 {
425 t!(f32x2, f32);
426 t!(f32x4, f32);
427 t!(f32x8, f32);
428 t!(f32x16, f32);
429 t!(f64x2, f64);
430 t!(f64x4, f64);
431 t!(f64x8, f64);
432 }
433 }
434
435 #[test]
436 fn test_uniform_from_std_range() {
437 let r = Uniform::try_from(2.0f64..7.0).unwrap();
438 assert_eq!(r.0.low, 2.0);
439 assert_eq!(r.0.scale, 5.0);
440 }
441
442 #[test]
443 fn test_uniform_from_std_range_bad_limits() {
444 #![allow(clippy::reversed_empty_ranges)]
445 assert!(Uniform::try_from(100.0..10.0).is_err());
446 assert!(Uniform::try_from(100.0..100.0).is_err());
447 }
448
449 #[test]
450 fn test_uniform_from_std_range_inclusive() {
451 let r = Uniform::try_from(2.0f64..=7.0).unwrap();
452 assert_eq!(r.0.low, 2.0);
453 assert!(r.0.scale > 5.0);
454 assert!(r.0.scale < 5.0 + 1e-14);
455 }
456
457 #[test]
458 fn test_uniform_from_std_range_inclusive_bad_limits() {
459 #![allow(clippy::reversed_empty_ranges)]
460 assert!(Uniform::try_from(100.0..=10.0).is_err());
461 assert!(Uniform::try_from(100.0..=99.0).is_err());
462 }
463}