Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

last bit of random f32 and f64 is not random #315

Closed
cake4289 opened this issue Mar 19, 2018 · 4 comments
Closed

last bit of random f32 and f64 is not random #315

cake4289 opened this issue Mar 19, 2018 · 4 comments

Comments

@cake4289
Copy link

The following "random" boolean generator will always print false:

extern crate rand;
use rand::Rng;

fn main() {
    let mut rng = rand::thread_rng();
    for _ in 0..1000 {
        let random = rng.gen::<f32>(); // or f64
        let random = if random >= 0.5 { // on 0.4.2 replace by `if false`
            random.to_bits() % 2 == 0
        } else {
            random.to_bits() % 2 == 1
        };
        println!("{}", random);
    }
}

Note that this is on master. On the latest release, the last bit is always 0. This is because of an algorithm change that changes the last bit for numbers between 0.5 and 1 by subtracting EPSILON / 2.0.

For smaller numbers it is perhaps ok that there are gaps, since then the possible random values are evenly spaced. But it seems wrong to miss off 1 bit of precision for all floating point values.

@dhardy
Copy link
Member

dhardy commented Mar 19, 2018

This is more of a limitation than a bug; precision of floating-point values is not (and wasn't previously) maximal. See #237 and dhardy#85.

Also see #308 which introduces a HighPrecision01 type.

@cake4289
Copy link
Author

Thanks @dhardy, if there is a high precision rng in the works for floats then that solves my issue, and I will just keep in mind the current limitations.

@dhardy
Copy link
Member

dhardy commented Mar 19, 2018

Good to hear.

The current HighPrecision01 adds an extra 9 / 12 bits of precision (for f32 / f64) but still doesn't get close to the limits of what the types can represent; we may improve on this later.

@pitdicker
Copy link
Contributor

pitdicker commented Mar 19, 2018

@cake4289 You can use the higher precision floating point methods with
let random = rng.sample::<f32, _>(HighPrecision01); (Edit: on current master).
And I agree, that is a good alternative. It is not really a 'high precision RNG', more a different and slightly slower conversion function.

May I ask you how you noticed the change? If you depend on some bits of a floating point value to be 1 or 0 with a 50% chance, and without patterns, there may be some trouble. Maybe I/we can suggest something if we see how you are using it...

The current HighPrecision01 adds an extra 9 / 12 bits of precision (for f32 / f64) but still doesn't get close to the limits of what the types can represent; we may improve on this later.

With the HighPrecision01 distribution only once every 2^10 (~1000) generated values it is possible to add 1 or more extra bits of randomness than what it currently has (for f32). And for f64 only once every 2^13 (8192) it it possible to fit one more bit of randomness in a f64. So I'd say we are pretty close to the limits 😄. But of course it is possible to go all the way down to subnormals.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants