// Copyright 2018 Developers of the Rand project. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Basic floating-point number distributions use core::mem; use Rng; use distributions::{Distribution, Standard}; use distributions::utils::FloatSIMDUtils; #[cfg(feature="simd_support")] use packed_simd::*; /// A distribution to sample floating point numbers uniformly in the half-open /// interval `(0, 1]`, i.e. including 1 but not 0. /// /// All values that can be generated are of the form `n * ε/2`. For `f32` /// the 23 most significant random bits of a `u32` are used and for `f64` the /// 53 most significant bits of a `u64` are used. The conversion uses the /// multiplicative method. /// /// See also: [`Standard`] which samples from `[0, 1)`, [`Open01`] /// which samples from `(0, 1)` and [`Uniform`] which samples from arbitrary /// ranges. /// /// # Example /// ``` /// use rand::{thread_rng, Rng}; /// use rand::distributions::OpenClosed01; /// /// let val: f32 = thread_rng().sample(OpenClosed01); /// println!("f32 from (0, 1): {}", val); /// ``` /// /// [`Standard`]: struct.Standard.html /// [`Open01`]: struct.Open01.html /// [`Uniform`]: uniform/struct.Uniform.html #[derive(Clone, Copy, Debug)] pub struct OpenClosed01; /// A distribution to sample floating point numbers uniformly in the open /// interval `(0, 1)`, i.e. not including either endpoint. /// /// All values that can be generated are of the form `n * ε + ε/2`. For `f32` /// the 22 most significant random bits of an `u32` are used, for `f64` 52 from /// an `u64`. The conversion uses a transmute-based method. /// /// See also: [`Standard`] which samples from `[0, 1)`, [`OpenClosed01`] /// which samples from `(0, 1]` and [`Uniform`] which samples from arbitrary /// ranges. /// /// # Example /// ``` /// use rand::{thread_rng, Rng}; /// use rand::distributions::Open01; /// /// let val: f32 = thread_rng().sample(Open01); /// println!("f32 from (0, 1): {}", val); /// ``` /// /// [`Standard`]: struct.Standard.html /// [`OpenClosed01`]: struct.OpenClosed01.html /// [`Uniform`]: uniform/struct.Uniform.html #[derive(Clone, Copy, Debug)] pub struct Open01; pub(crate) trait IntoFloat { type F; /// Helper method to combine the fraction and a contant exponent into a /// float. /// /// Only the least significant bits of `self` may be set, 23 for `f32` and /// 52 for `f64`. /// The resulting value will fall in a range that depends on the exponent. /// As an example the range with exponent 0 will be /// [20..21), which is [1..2). fn into_float_with_exponent(self, exponent: i32) -> Self::F; } macro_rules! float_impls { ($ty:ident, $uty:ident, $f_scalar:ident, $u_scalar:ty, $fraction_bits:expr, $exponent_bias:expr) => { impl IntoFloat for $uty { type F = $ty; #[inline(always)] fn into_float_with_exponent(self, exponent: i32) -> $ty { // The exponent is encoded using an offset-binary representation let exponent_bits: $u_scalar = (($exponent_bias + exponent) as $u_scalar) << $fraction_bits; // TODO: use from_bits when min compiler > 1.25 (see #545) // $ty::from_bits(self | exponent_bits) unsafe{ mem::transmute(self | exponent_bits) } } } impl Distribution<$ty> for Standard { fn sample(&self, rng: &mut R) -> $ty { // Multiply-based method; 24/53 random bits; [0, 1) interval. // We use the most significant bits because for simple RNGs // those are usually more random. let float_size = mem::size_of::<$f_scalar>() as u32 * 8; let precision = $fraction_bits + 1; let scale = 1.0 / ((1 as $u_scalar << precision) as $f_scalar); let value: $uty = rng.gen(); let value = value >> (float_size - precision); scale * $ty::cast_from_int(value) } } impl Distribution<$ty> for OpenClosed01 { fn sample(&self, rng: &mut R) -> $ty { // Multiply-based method; 24/53 random bits; (0, 1] interval. // We use the most significant bits because for simple RNGs // those are usually more random. let float_size = mem::size_of::<$f_scalar>() as u32 * 8; let precision = $fraction_bits + 1; let scale = 1.0 / ((1 as $u_scalar << precision) as $f_scalar); let value: $uty = rng.gen(); let value = value >> (float_size - precision); // Add 1 to shift up; will not overflow because of right-shift: scale * $ty::cast_from_int(value + 1) } } impl Distribution<$ty> for Open01 { fn sample(&self, rng: &mut R) -> $ty { // Transmute-based method; 23/52 random bits; (0, 1) interval. // We use the most significant bits because for simple RNGs // those are usually more random. use core::$f_scalar::EPSILON; let float_size = mem::size_of::<$f_scalar>() as u32 * 8; let value: $uty = rng.gen(); let fraction = value >> (float_size - $fraction_bits); fraction.into_float_with_exponent(0) - (1.0 - EPSILON / 2.0) } } } } float_impls! { f32, u32, f32, u32, 23, 127 } float_impls! { f64, u64, f64, u64, 52, 1023 } #[cfg(feature="simd_support")] float_impls! { f32x2, u32x2, f32, u32, 23, 127 } #[cfg(feature="simd_support")] float_impls! { f32x4, u32x4, f32, u32, 23, 127 } #[cfg(feature="simd_support")] float_impls! { f32x8, u32x8, f32, u32, 23, 127 } #[cfg(feature="simd_support")] float_impls! { f32x16, u32x16, f32, u32, 23, 127 } #[cfg(feature="simd_support")] float_impls! { f64x2, u64x2, f64, u64, 52, 1023 } #[cfg(feature="simd_support")] float_impls! { f64x4, u64x4, f64, u64, 52, 1023 } #[cfg(feature="simd_support")] float_impls! { f64x8, u64x8, f64, u64, 52, 1023 } #[cfg(test)] mod tests { use Rng; use distributions::{Open01, OpenClosed01}; use rngs::mock::StepRng; #[cfg(feature="simd_support")] use packed_simd::*; const EPSILON32: f32 = ::core::f32::EPSILON; const EPSILON64: f64 = ::core::f64::EPSILON; macro_rules! test_f32 { ($fnn:ident, $ty:ident, $ZERO:expr, $EPSILON:expr) => { #[test] fn $fnn() { // Standard let mut zeros = StepRng::new(0, 0); assert_eq!(zeros.gen::<$ty>(), $ZERO); let mut one = StepRng::new(1 << 8 | 1 << (8 + 32), 0); assert_eq!(one.gen::<$ty>(), $EPSILON / 2.0); let mut max = StepRng::new(!0, 0); assert_eq!(max.gen::<$ty>(), 1.0 - $EPSILON / 2.0); // OpenClosed01 let mut zeros = StepRng::new(0, 0); assert_eq!(zeros.sample::<$ty, _>(OpenClosed01), 0.0 + $EPSILON / 2.0); let mut one = StepRng::new(1 << 8 | 1 << (8 + 32), 0); assert_eq!(one.sample::<$ty, _>(OpenClosed01), $EPSILON); let mut max = StepRng::new(!0, 0); assert_eq!(max.sample::<$ty, _>(OpenClosed01), $ZERO + 1.0); // Open01 let mut zeros = StepRng::new(0, 0); assert_eq!(zeros.sample::<$ty, _>(Open01), 0.0 + $EPSILON / 2.0); let mut one = StepRng::new(1 << 9 | 1 << (9 + 32), 0); assert_eq!(one.sample::<$ty, _>(Open01), $EPSILON / 2.0 * 3.0); let mut max = StepRng::new(!0, 0); assert_eq!(max.sample::<$ty, _>(Open01), 1.0 - $EPSILON / 2.0); } } } test_f32! { f32_edge_cases, f32, 0.0, EPSILON32 } #[cfg(feature="simd_support")] test_f32! { f32x2_edge_cases, f32x2, f32x2::splat(0.0), f32x2::splat(EPSILON32) } #[cfg(feature="simd_support")] test_f32! { f32x4_edge_cases, f32x4, f32x4::splat(0.0), f32x4::splat(EPSILON32) } #[cfg(feature="simd_support")] test_f32! { f32x8_edge_cases, f32x8, f32x8::splat(0.0), f32x8::splat(EPSILON32) } #[cfg(feature="simd_support")] test_f32! { f32x16_edge_cases, f32x16, f32x16::splat(0.0), f32x16::splat(EPSILON32) } macro_rules! test_f64 { ($fnn:ident, $ty:ident, $ZERO:expr, $EPSILON:expr) => { #[test] fn $fnn() { // Standard let mut zeros = StepRng::new(0, 0); assert_eq!(zeros.gen::<$ty>(), $ZERO); let mut one = StepRng::new(1 << 11, 0); assert_eq!(one.gen::<$ty>(), $EPSILON / 2.0); let mut max = StepRng::new(!0, 0); assert_eq!(max.gen::<$ty>(), 1.0 - $EPSILON / 2.0); // OpenClosed01 let mut zeros = StepRng::new(0, 0); assert_eq!(zeros.sample::<$ty, _>(OpenClosed01), 0.0 + $EPSILON / 2.0); let mut one = StepRng::new(1 << 11, 0); assert_eq!(one.sample::<$ty, _>(OpenClosed01), $EPSILON); let mut max = StepRng::new(!0, 0); assert_eq!(max.sample::<$ty, _>(OpenClosed01), $ZERO + 1.0); // Open01 let mut zeros = StepRng::new(0, 0); assert_eq!(zeros.sample::<$ty, _>(Open01), 0.0 + $EPSILON / 2.0); let mut one = StepRng::new(1 << 12, 0); assert_eq!(one.sample::<$ty, _>(Open01), $EPSILON / 2.0 * 3.0); let mut max = StepRng::new(!0, 0); assert_eq!(max.sample::<$ty, _>(Open01), 1.0 - $EPSILON / 2.0); } } } test_f64! { f64_edge_cases, f64, 0.0, EPSILON64 } #[cfg(feature="simd_support")] test_f64! { f64x2_edge_cases, f64x2, f64x2::splat(0.0), f64x2::splat(EPSILON64) } #[cfg(feature="simd_support")] test_f64! { f64x4_edge_cases, f64x4, f64x4::splat(0.0), f64x4::splat(EPSILON64) } #[cfg(feature="simd_support")] test_f64! { f64x8_edge_cases, f64x8, f64x8::splat(0.0), f64x8::splat(EPSILON64) } }