use core::mem;
use Rng;
use distributions::{Distribution, Standard};
#[derive(Clone, Copy, Debug)]
pub struct OpenClosed01;
#[derive(Clone, Copy, Debug)]
pub struct Open01;
pub(crate) trait IntoFloat {
type F;
fn into_float_with_exponent(self, exponent: i32) -> Self::F;
}
macro_rules! float_impls {
($ty:ty, $uty: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 {
let exponent_bits =
(($exponent_bias + exponent) as $uty) << $fraction_bits;
unsafe { mem::transmute(self | exponent_bits) }
}
}
impl Distribution<$ty> for Standard {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $ty {
let float_size = mem::size_of::<$ty>() * 8;
let precision = $fraction_bits + 1;
let scale = 1.0 / ((1 as $uty << precision) as $ty);
let value: $uty = rng.gen();
scale * (value >> (float_size - precision)) as $ty
}
}
impl Distribution<$ty> for OpenClosed01 {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $ty {
let float_size = mem::size_of::<$ty>() * 8;
let precision = $fraction_bits + 1;
let scale = 1.0 / ((1 as $uty << precision) as $ty);
let value: $uty = rng.gen();
let value = value >> (float_size - precision);
scale * (value + 1) as $ty
}
}
impl Distribution<$ty> for Open01 {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $ty {
const EPSILON: $ty = 1.0 / (1u64 << $fraction_bits) as $ty;
let float_size = mem::size_of::<$ty>() * 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, 23, 127 }
float_impls! { f64, u64, 52, 1023 }
#[cfg(test)]
mod tests {
use Rng;
use distributions::{Open01, OpenClosed01};
use rngs::mock::StepRng;
const EPSILON32: f32 = ::core::f32::EPSILON;
const EPSILON64: f64 = ::core::f64::EPSILON;
#[test]
fn standard_fp_edge_cases() {
let mut zeros = StepRng::new(0, 0);
assert_eq!(zeros.gen::<f32>(), 0.0);
assert_eq!(zeros.gen::<f64>(), 0.0);
let mut one32 = StepRng::new(1 << 8, 0);
assert_eq!(one32.gen::<f32>(), EPSILON32 / 2.0);
let mut one64 = StepRng::new(1 << 11, 0);
assert_eq!(one64.gen::<f64>(), EPSILON64 / 2.0);
let mut max = StepRng::new(!0, 0);
assert_eq!(max.gen::<f32>(), 1.0 - EPSILON32 / 2.0);
assert_eq!(max.gen::<f64>(), 1.0 - EPSILON64 / 2.0);
}
#[test]
fn openclosed01_edge_cases() {
let mut zeros = StepRng::new(0, 0);
assert_eq!(zeros.sample::<f32, _>(OpenClosed01), 0.0 + EPSILON32 / 2.0);
assert_eq!(zeros.sample::<f64, _>(OpenClosed01), 0.0 + EPSILON64 / 2.0);
let mut one32 = StepRng::new(1 << 8, 0);
assert_eq!(one32.sample::<f32, _>(OpenClosed01), EPSILON32);
let mut one64 = StepRng::new(1 << 11, 0);
assert_eq!(one64.sample::<f64, _>(OpenClosed01), EPSILON64);
let mut max = StepRng::new(!0, 0);
assert_eq!(max.sample::<f32, _>(OpenClosed01), 1.0);
assert_eq!(max.sample::<f64, _>(OpenClosed01), 1.0);
}
#[test]
fn open01_edge_cases() {
let mut zeros = StepRng::new(0, 0);
assert_eq!(zeros.sample::<f32, _>(Open01), 0.0 + EPSILON32 / 2.0);
assert_eq!(zeros.sample::<f64, _>(Open01), 0.0 + EPSILON64 / 2.0);
let mut one32 = StepRng::new(1 << 9, 0);
assert_eq!(one32.sample::<f32, _>(Open01), EPSILON32 / 2.0 * 3.0);
let mut one64 = StepRng::new(1 << 12, 0);
assert_eq!(one64.sample::<f64, _>(Open01), EPSILON64 / 2.0 * 3.0);
let mut max = StepRng::new(!0, 0);
assert_eq!(max.sample::<f32, _>(Open01), 1.0 - EPSILON32 / 2.0);
assert_eq!(max.sample::<f64, _>(Open01), 1.0 - EPSILON64 / 2.0);
}
}