Skip to content

Output of gen_range is platform dependent for usize #1399

@ttencate

Description

@ttencate

The output of Rng::gen_range is different depending on whether usize is 32 bits or 64 bits on the current platform, even if the requested range is small (0..12 in my case). For example:

    use rand::Rng;
    use rand_core::SeedableRng;

    let mut rng = rand_pcg::Pcg32::seed_from_u64(0);
    for _ in 0..16 {
        let x: usize = rng.gen_range(0..12);
        print!("{} ", x);
    }
    println!();

On a 64-bits system, the output is: 4 8 6 0 5 1 5 8 9 10 11 7 6 11 1 8
On a 32-bits system, the output is: 4 1 8 0 6 1 0 8 5 11 1 9 5 5 8 6

This does not affect SliceRandom::choose, thanks to some special handling in gen_index:

rand/src/seq/mod.rs

Lines 699 to 710 in 7ff0fc9

// Sample a number uniformly between 0 and `ubound`. Uses 32-bit sampling where
// possible, primarily in order to produce the same output on 32-bit and 64-bit
// platforms.
#[inline]
fn gen_index<R: Rng + ?Sized>(rng: &mut R, ubound: usize) -> usize {
if ubound <= (core::u32::MAX as usize) {
rng.gen_range(0..ubound as u32) as usize
} else {
rng.gen_range(0..ubound)
}
}

This means that, contrary to my expectations, my_slice.choose(&mut rng) and my_slice[rng.gen_range(0..my_slice.len())] are not equivalent (even ignoring the case of an empty slice).

I'm not sure if this qualifies as a bug, but it definitely surprised me, and took me some time to debug. I'm also not sure if it can be fixed, and if fixing it would constitute a breaking change.

In any case it could be documented, here in the book and here on Rng::gen_range.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions