Skip to content
33 changes: 29 additions & 4 deletions src/distributions/range.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ use distributions::float::IntoFloat;
/// use rand::distributions::{Distribution, Range};
///
/// fn main() {
/// let between = Range::new(10, 10000);
/// let between = Range::from(10..10000);
/// let mut rng = rand::thread_rng();
/// let mut sum = 0;
/// for _ in 0..1000 {
Expand Down Expand Up @@ -173,6 +173,9 @@ pub trait RangeImpl: Sized {
}

/// Implementation of `RangeImpl` for integer types.
///
/// Unless you are implementing `RangeImpl` for your own type, this type should
/// not be used directly, use `Range` instead.
#[derive(Clone, Copy, Debug)]
pub struct RangeInt<X> {
low: X,
Expand Down Expand Up @@ -317,6 +320,12 @@ macro_rules! range_int_impl {
}
}

impl<X: SampleRange> From<::core::ops::Range<X>> for Range<X> {
fn from(r: ::core::ops::Range<X>) -> Range<X> {
Range::new(r.start, r.end)
}
}

range_int_impl! { i8, i8, u8, i32, u32 }
range_int_impl! { i16, i16, u16, i32, u32 }
range_int_impl! { i32, i32, u32, i32, u32 }
Expand Down Expand Up @@ -420,6 +429,9 @@ wmul_impl_usize! { u64 }


/// Implementation of `RangeImpl` for float types.
///
/// Unless you are implementing `RangeImpl` for your own type, this type should
/// not be used directly, use `Range` instead.
#[derive(Clone, Copy, Debug)]
pub struct RangeFloat<X> {
scale: X,
Expand Down Expand Up @@ -454,8 +466,11 @@ macro_rules! range_float_impl {
// Generate a value in the range [1, 2)
let value1_2 = (rng.$next_u() >> $bits_to_discard)
.into_float_with_exponent(0);
// Doing multiply before addition allows some architectures to
// use a single instruction.
Copy link
Contributor

@pitdicker pitdicker Apr 12, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before #274 we would do r.low + r.range * rng.gen::<$ty>(). I think it is nice to preserve this comment that explains why we choose a different order.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please be more specific? I'm not sure the previous comment was correct. Without fast-math it is not correct if the compiler uses FMA.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, there is an instruction that does a true FMA, with the benefit of extra precision, and a simple instruction that produuces exactly the same results as multiply and addition. At least on ARM.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I'll add a comment!

// We don't use `f64::mul_add`, because it is not available with
// `no_std`. Furthermore, it is slower for some targets (but
// faster for others). However, the order of multiplication and
// addition is important, because on some platforms (e.g. ARM)
// it will be optimized to a single (non-FMA) instruction.
value1_2 * self.scale + self.offset
}
}
Expand All @@ -469,7 +484,7 @@ range_float_impl! { f64, 64 - 52, next_u64 }
#[cfg(test)]
mod tests {
use Rng;
use distributions::range::{Range, RangeImpl, RangeFloat, SampleRange};
use distributions::range::{Range, RangeImpl, RangeInt, RangeFloat, SampleRange};

#[should_panic]
#[test]
Expand Down Expand Up @@ -578,4 +593,14 @@ mod tests {
assert!(low <= x && x < high);
}
}

#[test]
fn test_range_from_std_range() {
let r = Range::from(2u32..7);
assert_eq!(r.inner.low, 2);
assert_eq!(r.inner.range, 5);
let r = Range::from(2.0f64..7.0);
assert_eq!(r.inner.offset, -3.0);
assert_eq!(r.inner.scale, 5.0);
}
}