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:
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).
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.