Skip to content

Commit b3fd8d0

Browse files
committed
Simplify/fix zone calculation for small integers
1 parent 316761d commit b3fd8d0

File tree

1 file changed

+11
-19
lines changed

1 file changed

+11
-19
lines changed

src/distributions/uniform.rs

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -288,17 +288,12 @@ impl<X: SampleUniform> From<::core::ops::Range<X>> for Uniform<X> {
288288
/// `ints_to_reject = (unsigned_max - range + 1) % range;`
289289
///
290290
/// The smallest integer PRNGs generate is `u32`. That is why for small integer
291-
/// sizes (`i8`/`u8` and `i16`/`u16`) there is an optimisation: don't pick the
291+
/// sizes (`i8`/`u8` and `i16`/`u16`) there is an optimization: don't pick the
292292
/// largest zone that can fit in the small type, but pick the largest zone that
293-
/// can fit in an `u32`. This improves the chance to get a random integer that
294-
/// fits in the zone to 998 in 1000 in the worst case.
295-
///
296-
/// There is a problem however: we can't store the acceptable `zone` of such a
297-
/// larger type in `UniformInt`, which only holds values with the size of the
298-
/// type. `ints_to_reject` is always less than half the size of the small
299-
/// integer. For an `u8` it only ever uses 7 bits. This means that all but the
300-
/// last 7 bits of `zone` are always 1's (or 15 in the case of `u16`). So
301-
/// nothing is lost by trucating `zone`.
293+
/// can fit in an `u32`. `ints_to_reject` is always less than half the size of
294+
/// the small integer. This means the first bit of `zone` is always 1, and so
295+
/// are all the other preceding bits of a larger integer. The easiest way to
296+
/// grow the `zone` for the larger type is to simply sign extend it.
302297
///
303298
/// An alternative to using a modulus is widening multiply: After a widening
304299
/// multiply by `range`, the result is in the high word. Then comparing the low
@@ -341,11 +336,9 @@ macro_rules! uniform_int_impl {
341336
fn new_inclusive(low: Self::X, high: Self::X) -> Self {
342337
assert!(low <= high,
343338
"Uniform::new_inclusive called with `low > high`");
344-
let unsigned_max: $u_large = ::core::$u_large::MAX;
339+
let unsigned_max = ::core::$unsigned::MAX;
345340

346-
let range = (high as $u_large)
347-
.wrapping_sub(low as $u_large)
348-
.wrapping_add(1);
341+
let range = high.wrapping_sub(low).wrapping_add(1) as $unsigned;
349342
let ints_to_reject =
350343
if range > 0 {
351344
(unsigned_max - range + 1) % range
@@ -365,9 +358,9 @@ macro_rules! uniform_int_impl {
365358
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Self::X {
366359
let range = self.range as $unsigned as $u_large;
367360
if range > 0 {
368-
// Some casting to recover the trucated bits of `zone`:
369-
// First bit-cast to a signed int. Next sign-extend to the
370-
// larger type. Then bit-cast to unsigned.
361+
// Grow `zone` to fit a type of at least 32 bits, by
362+
// sign-extending it (the first bit is always 1, so are all
363+
// the preceding bits of the larger type).
371364
// For types that already have the right size, all the
372365
// casting is a no-op.
373366
let zone = self.zone as $signed as $i_large as $u_large;
@@ -390,8 +383,7 @@ macro_rules! uniform_int_impl {
390383
{
391384
assert!(low < high,
392385
"Uniform::sample_single called with low >= high");
393-
let range = (high as $u_large)
394-
.wrapping_sub(low as $u_large);
386+
let range = high.wrapping_sub(low) as $unsigned as $u_large;
395387
let zone =
396388
if ::core::$unsigned::MAX <= ::core::u16::MAX as $unsigned {
397389
// Using a modulus is faster than the approximation for

0 commit comments

Comments
 (0)