when using "chained" double generators with jqwik I get a scale error message java.util.concurrent.ExecutionException: net.jqwik.api.JqwikException: Decimal value -1.6099999999999999 cannot be represented with scale 4.
.
Can you provide me with some details on how to set this scale and the meaning of this parameter ?
Here is the generator function I use :
@Provide("close doubles")
Arbitrary<Tuple.Tuple2<Double,Double>> closeDoubles(@ForAll() Double aDouble) {
return Arbitraries.doubles()
.between(aDouble-2.5, aDouble+2.5)
.withSpecialValue(aDouble)
.ofScale(4)
.map(num ->Tuple.of(aDouble,num));
}
It is then combined to form a business object instance.
My ultimate goal is to generate 2 doubles that are "close" to each other (here the distance is 2.5).
The problem you encounter is due to rounding errors of double numbers and the fact that jqwik is strict with allowing only upper and lower boundaries that comply with the specified scale.
I see several options to get around that, one is to use BigDecimals for generation and map them to double afterwards. This may look like overhead but actually it is not because that's what jqwik is doing anyway under the hood. This could look like:
@Provide
Arbitrary<Tuple.Tuple2<Double, Double>> closeDoubles(@ForAll @Scale(4) BigDecimal aBigDecimal) {
BigDecimal twoPointFive = new BigDecimal("2.5");
return Arbitraries.bigDecimals().between(aBigDecimal.subtract(twoPointFive), aBigDecimal.add(twoPointFive))
.ofScale(4)
.map(num -> Tuple.of(aBigDecimal.doubleValue(), num.doubleValue()));
}
Mind that the original number should also use the same scale as the target numbers, otherwise it will have a default scale of 2.
Personally, I'd prefer to generate a number and the delta, which has improved shrinking behaviour and will create a tuple with identical numbers more often:
@Provide
Arbitrary<Tuple.Tuple2<Double, Double>> closeDoubles2(@ForAll @Scale(4) BigDecimal aBigDecimal) {
BigDecimal twoPointFive = new BigDecimal("2.5");
return Arbitraries.bigDecimals().between(twoPointFive.negate(), twoPointFive)
.ofScale(4)
.map(num -> Tuple.of(aBigDecimal.doubleValue(), aBigDecimal.add(num).doubleValue()));
}