Skip to content

Commit 98b915f

Browse files
committed
Change gen_range to accept Range types
This adds support for `Rng::range(a..b)` and `Rng::range(a..=b)`, replacing `Rng::range(a, b)`. `a` and `b` must now implement `PartialEq`. This is a breaking change. In most cases, replacing `Rng::gen_range(a, b)` with `Rng::gen_range(a..b)` should be enough, however type annotations may be necessary, and `a` and `b` can no longer be references or SIMD types. SIMD types are still supported by `UniformSampler::sample_single`. Some clippy warnings were fixed as well.
1 parent 17651c0 commit 98b915f

File tree

8 files changed

+145
-66
lines changed

8 files changed

+145
-66
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ You may also find the [Upgrade Guide](https://rust-random.github.io/book/update.
1313
- impl PartialEq+Eq for StdRng, SmallRng, and StepRng (#975)
1414
- Added a `serde1` feature and added Serialize/Deserialize to `UniformInt` and `WeightedIndex` (#974)
1515

16+
### Changes
17+
- `gen_range(a, b)` was replaced with `gen_range(a..b)`, and `gen_range(a..=b)`
18+
is supported (#744, #1003). Note that `a` and `b` can no longer be references.
19+
1620
## [0.7.3] - 2020-01-10
1721
### Fixes
1822
- The `Bernoulli` distribution constructors now reports an error on NaN and on

benches/distributions.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ macro_rules! gen_range_int {
197197
let mut high = $high;
198198
let mut accum: $ty = 0;
199199
for _ in 0..RAND_BENCH_N {
200-
accum = accum.wrapping_add(rng.gen_range($low, high));
200+
accum = accum.wrapping_add(rng.gen_range($low..high));
201201
// force recalculation of range each time
202202
high = high.wrapping_add(1) & std::$ty::MAX;
203203
}
@@ -237,7 +237,7 @@ macro_rules! gen_range_float {
237237
let mut low = $low;
238238
let mut accum: $ty = 0.0;
239239
for _ in 0..RAND_BENCH_N {
240-
accum += rng.gen_range(low, high);
240+
accum += rng.gen_range(low..high);
241241
// force recalculation of range each time
242242
low += 0.9;
243243
high += 1.1;

src/distributions/mod.rs

+10-11
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
//!
1212
//! This module is the home of the [`Distribution`] trait and several of its
1313
//! implementations. It is the workhorse behind some of the convenient
14-
//! functionality of the [`Rng`] trait, e.g. [`Rng::gen`], [`Rng::gen_range`] and
15-
//! of course [`Rng::sample`].
14+
//! functionality of the [`Rng`] trait, e.g. [`Rng::gen`] and of course
15+
//! [`Rng::sample`].
1616
//!
1717
//! Abstractly, a [probability distribution] describes the probability of
1818
//! occurrence of each value in its sample space.
@@ -54,16 +54,16 @@
5454
//! space to be specified as an arbitrary range within its target type `T`.
5555
//! Both [`Standard`] and [`Uniform`] are in some sense uniform distributions.
5656
//!
57-
//! Values may be sampled from this distribution using [`Rng::gen_range`] or
57+
//! Values may be sampled from this distribution using [`Rng::sample(Range)`] or
5858
//! by creating a distribution object with [`Uniform::new`],
5959
//! [`Uniform::new_inclusive`] or `From<Range>`. When the range limits are not
6060
//! known at compile time it is typically faster to reuse an existing
61-
//! distribution object than to call [`Rng::gen_range`].
61+
//! `Uniform` object than to call [`Rng::sample(Range)`].
6262
//!
6363
//! User types `T` may also implement `Distribution<T>` for [`Uniform`],
6464
//! although this is less straightforward than for [`Standard`] (see the
65-
//! documentation in the [`uniform`] module. Doing so enables generation of
66-
//! values of type `T` with [`Rng::gen_range`].
65+
//! documentation in the [`uniform`] module). Doing so enables generation of
66+
//! values of type `T` with [`Rng::sample(Range)`].
6767
//!
6868
//! ## Open and half-open ranges
6969
//!
@@ -315,11 +315,10 @@ where
315315
/// multiplicative method: `(rng.gen::<$uty>() >> N) as $ty * (ε/2)`.
316316
///
317317
/// See also: [`Open01`] which samples from `(0, 1)`, [`OpenClosed01`] which
318-
/// samples from `(0, 1]` and `Rng::gen_range(0, 1)` which also samples from
319-
/// `[0, 1)`. Note that `Open01` and `gen_range` (which uses [`Uniform`]) use
320-
/// transmute-based methods which yield 1 bit less precision but may perform
321-
/// faster on some architectures (on modern Intel CPUs all methods have
322-
/// approximately equal performance).
318+
/// samples from `(0, 1]` and `Rng::gen_range(0..1)` which also samples from
319+
/// `[0, 1)`. Note that `Open01` uses transmute-based methods which yield 1 bit
320+
/// less precision but may perform faster on some architectures (on modern Intel
321+
/// CPUs all methods have approximately equal performance).
323322
///
324323
/// [`Uniform`]: uniform::Uniform
325324
#[derive(Clone, Copy, Debug)]

src/distributions/uniform.rs

+76-23
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2018 Developers of the Rand project.
1+
// Copyright 2018-2020 Developers of the Rand project.
22
// Copyright 2017 The Rust Project Developers.
33
//
44
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
@@ -34,7 +34,7 @@
3434
//! let side = Uniform::new(-10.0, 10.0);
3535
//!
3636
//! // sample between 1 and 10 points
37-
//! for _ in 0..rng.gen_range(1, 11) {
37+
//! for _ in 0..rng.gen_range(1..=10) {
3838
//! // sample a point from the square with sides -10 - 10 in two dimensions
3939
//! let (x, y) = (rng.sample(side), rng.sample(side));
4040
//! println!("Point: {}, {}", x, y);
@@ -105,11 +105,12 @@
105105
106106
#[cfg(not(feature = "std"))] use core::time::Duration;
107107
#[cfg(feature = "std")] use std::time::Duration;
108+
use core::ops::{Range, RangeInclusive};
108109

109110
use crate::distributions::float::IntoFloat;
110111
use crate::distributions::utils::{BoolAsSIMD, FloatAsSIMD, FloatSIMDUtils, WideningMultiply};
111112
use crate::distributions::Distribution;
112-
use crate::Rng;
113+
use crate::{Rng, RngCore};
113114

114115
#[cfg(not(feature = "std"))]
115116
#[allow(unused_imports)] // rustc doesn't detect that this is actually used
@@ -124,7 +125,8 @@ use serde::{Serialize, Deserialize};
124125
///
125126
/// [`Uniform::new`] and [`Uniform::new_inclusive`] construct a uniform
126127
/// distribution sampling from the given range; these functions may do extra
127-
/// work up front to make sampling of multiple values faster.
128+
/// work up front to make sampling of multiple values faster. If only one sample
129+
/// from the range is required, [`Rng::gen_range`] can be more efficient.
128130
///
129131
/// When sampling from a constant range, many calculations can happen at
130132
/// compile-time and all methods should be fast; for floating-point ranges and
@@ -139,27 +141,35 @@ use serde::{Serialize, Deserialize};
139141
/// are of lower quality than the high bits.
140142
///
141143
/// Implementations must sample in `[low, high)` range for
142-
/// `Uniform::new(low, high)`, i.e., excluding `high`. In particular care must
144+
/// `Uniform::new(low, high)`, i.e., excluding `high`. In particular, care must
143145
/// be taken to ensure that rounding never results values `< low` or `>= high`.
144146
///
145147
/// # Example
146148
///
147149
/// ```
148150
/// use rand::distributions::{Distribution, Uniform};
149151
///
150-
/// fn main() {
151-
/// let between = Uniform::from(10..10000);
152-
/// let mut rng = rand::thread_rng();
153-
/// let mut sum = 0;
154-
/// for _ in 0..1000 {
155-
/// sum += between.sample(&mut rng);
156-
/// }
157-
/// println!("{}", sum);
152+
/// let between = Uniform::from(10..10000);
153+
/// let mut rng = rand::thread_rng();
154+
/// let mut sum = 0;
155+
/// for _ in 0..1000 {
156+
/// sum += between.sample(&mut rng);
158157
/// }
158+
/// println!("{}", sum);
159+
/// ```
160+
///
161+
/// For a single sample, [`Rng::gen_range`] may be prefered:
162+
///
163+
/// ```
164+
/// use rand::Rng;
165+
///
166+
/// let mut rng = rand::thread_rng();
167+
/// println!("{}", rng.gen_range(0..10));
159168
/// ```
160169
///
161170
/// [`new`]: Uniform::new
162171
/// [`new_inclusive`]: Uniform::new_inclusive
172+
/// [`Rng::gen_range`]: Rng::gen_range
163173
#[derive(Clone, Copy, Debug)]
164174
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
165175
pub struct Uniform<X: SampleUniform>(X::Sampler);
@@ -287,18 +297,19 @@ pub trait UniformSampler: Sized {
287297
}
288298
}
289299

290-
impl<X: SampleUniform> From<::core::ops::Range<X>> for Uniform<X> {
300+
impl<X: SampleUniform> From<Range<X>> for Uniform<X> {
291301
fn from(r: ::core::ops::Range<X>) -> Uniform<X> {
292302
Uniform::new(r.start, r.end)
293303
}
294304
}
295305

296-
impl<X: SampleUniform> From<::core::ops::RangeInclusive<X>> for Uniform<X> {
306+
impl<X: SampleUniform> From<RangeInclusive<X>> for Uniform<X> {
297307
fn from(r: ::core::ops::RangeInclusive<X>) -> Uniform<X> {
298308
Uniform::new_inclusive(r.start(), r.end())
299309
}
300310
}
301311

312+
302313
/// Helper trait similar to [`Borrow`] but implemented
303314
/// only for SampleUniform and references to SampleUniform in
304315
/// order to resolve ambiguity issues.
@@ -327,6 +338,43 @@ where Borrowed: SampleUniform
327338
}
328339
}
329340

341+
/// Range that supports generating a single sample efficiently.
342+
///
343+
/// Any type implementing this trait can be used to specify the sampled range
344+
/// for `Rng::gen_range`.
345+
pub trait SampleRange<T> {
346+
/// Generate a sample from the given range.
347+
fn sample_single<R: RngCore + ?Sized>(self, rng: &mut R) -> T;
348+
349+
/// Check whether the range is empty.
350+
fn is_empty(&self) -> bool;
351+
}
352+
353+
impl<T: SampleUniform + PartialOrd> SampleRange<T> for Range<T> {
354+
#[inline]
355+
fn sample_single<R: RngCore + ?Sized>(self, rng: &mut R) -> T {
356+
T::Sampler::sample_single(self.start, self.end, rng)
357+
}
358+
359+
#[inline]
360+
fn is_empty(&self) -> bool {
361+
!(self.start < self.end)
362+
}
363+
}
364+
365+
impl<T: SampleUniform + PartialOrd> SampleRange<T> for RangeInclusive<T> {
366+
#[inline]
367+
fn sample_single<R: RngCore + ?Sized>(self, rng: &mut R) -> T {
368+
T::Sampler::sample_single_inclusive(self.start(), self.end(), rng)
369+
}
370+
371+
#[inline]
372+
fn is_empty(&self) -> bool {
373+
!(self.start() <= self.end())
374+
}
375+
}
376+
377+
330378
////////////////////////////////////////////////////////////////////////////////
331379

332380
// What follows are all back-ends.
@@ -425,7 +473,7 @@ macro_rules! uniform_int_impl {
425473
};
426474

427475
UniformInt {
428-
low: low,
476+
low,
429477
// These are really $unsigned values, but store as $ty:
430478
range: range as $ty,
431479
z: ints_to_reject as $unsigned as $ty,
@@ -570,7 +618,7 @@ macro_rules! uniform_simd_int_impl {
570618
let zone = unsigned_max - ints_to_reject;
571619

572620
UniformInt {
573-
low: low,
621+
low,
574622
// These are really $unsigned values, but store as $ty:
575623
range: range.cast(),
576624
z: zone.cast(),
@@ -985,7 +1033,7 @@ impl UniformSampler for UniformDuration {
9851033
max_nanos,
9861034
secs,
9871035
} => {
988-
// constant folding means this is at least as fast as `gen_range`
1036+
// constant folding means this is at least as fast as `Rng::sample(Range)`
9891037
let nano_range = Uniform::new(0, 1_000_000_000);
9901038
loop {
9911039
let s = secs.sample(rng);
@@ -1112,7 +1160,7 @@ mod tests {
11121160
}
11131161

11141162
for _ in 0..1000 {
1115-
let v: $ty = rng.gen_range(low, high);
1163+
let v = <$ty as SampleUniform>::Sampler::sample_single(low, high, &mut rng);
11161164
assert!($le(low, v) && $lt(v, high));
11171165
}
11181166
}
@@ -1194,7 +1242,8 @@ mod tests {
11941242
assert!(low_scalar <= v && v < high_scalar);
11951243
let v = rng.sample(my_incl_uniform).extract(lane);
11961244
assert!(low_scalar <= v && v <= high_scalar);
1197-
let v = rng.gen_range(low, high).extract(lane);
1245+
let v = <$ty as SampleUniform>::Sampler
1246+
::sample_single(low, high, &mut rng).extract(lane);
11981247
assert!(low_scalar <= v && v < high_scalar);
11991248
}
12001249

@@ -1205,7 +1254,9 @@ mod tests {
12051254

12061255
assert_eq!(zero_rng.sample(my_uniform).extract(lane), low_scalar);
12071256
assert_eq!(zero_rng.sample(my_incl_uniform).extract(lane), low_scalar);
1208-
assert_eq!(zero_rng.gen_range(low, high).extract(lane), low_scalar);
1257+
assert_eq!(<$ty as SampleUniform>::Sampler
1258+
::sample_single(low, high, &mut zero_rng)
1259+
.extract(lane), low_scalar);
12091260
assert!(max_rng.sample(my_uniform).extract(lane) < high_scalar);
12101261
assert!(max_rng.sample(my_incl_uniform).extract(lane) <= high_scalar);
12111262

@@ -1218,7 +1269,9 @@ mod tests {
12181269
(-1i64 << $bits_shifted) as u64,
12191270
);
12201271
assert!(
1221-
lowering_max_rng.gen_range(low, high).extract(lane) < high_scalar
1272+
<$ty as SampleUniform>::Sampler
1273+
::sample_single(low, high, &mut lowering_max_rng)
1274+
.extract(lane) < high_scalar
12221275
);
12231276
}
12241277
}
@@ -1266,7 +1319,7 @@ mod tests {
12661319
use std::panic::catch_unwind;
12671320
fn range<T: SampleUniform>(low: T, high: T) {
12681321
let mut rng = crate::test::rng(253);
1269-
rng.gen_range(low, high);
1322+
T::Sampler::sample_single(low, high, &mut rng);
12701323
}
12711324

12721325
macro_rules! t {

0 commit comments

Comments
 (0)