How to limit floats generated by Quickcheck to a range?

Viewed 421

I would like to generate random floats for Quickcheck that are limited to a certain range such as 0.0 to 1.0 for testing functions working on probabilities. I would like to be able to do something where this would be successful:

quickcheck! {        
    fn prop(x: f64, y: f64) -> bool {
        assert!(x <= 1.0);
        assert!(y <= 1.0);

        (x * y < x) && (x * y < y)
    }
}
2 Answers

Implementing Arbitrary for a new type is still probably the best way to do this, but gen_range() is now a private method, so you can't use that. As pointed out by the crate's author, you can use the modulus to restrict values to a range.

You can even do this without creating a new type (note that I'm using newer Rust syntax and the quickcheck_macros crate):

#[quickcheck]
fn prop(x: f64, y: f64) -> bool {
    let x = x.abs() % 1.0;
    let y = y.abs() % 1.0;
    assert!(x <= 1.0);
    assert!(y <= 1.0);

    (x * y < x) && (x * y < y)
}

However, when it fails, it will report the original values of x and y, not the modified ones. So for better test failure reporting, you should use this in code similar to @Shepmaster's:

#[derive(Debug, Copy, Clone)]
struct Probability(f64);

impl quickcheck::Arbitrary for Probability {
    fn arbitrary(g: &mut quickcheck::Gen) -> Self {
        Probability(f64::arbitrary(g).abs() % 1.0)
    }
}

#[quickcheck]
fn prop2(x: Probability, y: Probability) {
    let x = x.0;
    let y = y.0;

    // ...
}

Of course, once you do all this, you'll notice your code failing for two reasons:

  1. Since x and y could be exactly equal to 1.0, that means it's not a strict inequality. The true comparison should actually be (x * y <= x) && (x * y <= y).

  2. f64::arbitrary can return NaN. If you want to skip NaNs, you could do that by either:

    • Returning quickcheck::TestCase from prop(), and returning TestCase::discard() if it sees a NaN input, or:

    • Looping inside Probability::arbitrary until you generate a non-NaN number.

Related