I'm trying to construct an algorithm that validates that a double value is a member of a range defined with min, max and step values. The problem is checking that the value obeys the step rule. For integers this can be easily done:
boolean validate(int value, int min, int step, int max){
return value >= min &&
value <= max &&
//Step should be relative to the min value not to 0.
(value-min) % step == 0;
}
This however doesn't work for double values. I know that this at least partly is for precision reasons and I tried hacking a solution by multiplying all the values by a very high number and convert them to longs. This didn't work for all the values though, and neither did allowing a small deviance from 0 when checking the remainder. Has anyone had this problem and come up with a good solution? Below is an example and test featuring the non-working validation method.
One method of doing this would be to start at the min value and increment it by step until it is equal to or greater that the input value, but apart from beeing an ugly solution, this could be a potential bottleneck in my app, so I really want to avoid it.
I'm grateful for any pointers...
Regards / Henrik
public class ValidationExample {
public static void main(String[] args) {
/*Range:
min -10.5
step .3
max -5
*/
//Invalid values
double[] a = {-11,-10.6,-10.4,-10.3,-10.1,-10.0,-9.8,-9.7,
-9.5,-9.4,-9.2,-9.1,-8.9,-8.8,-8.6,-8.5,-8.3,
-8.2,-8,-7.9,-7.7,-7.6,-7.4,-7.3,-7.1,-7.0,
-6.8,-6.7,-6.5,-6.4,-6.2,-6.1,-5.9,-5.8,-5.6,
-5.5,-5.3,-5.2,-5.0,-4.9,-4.8,2};
//Valid values
double[] b = {-10.5,-10.2,-9.9,-9.6,-9.3,-9.0,-8.7,-8.4,
-8.1,-7.8,-7.5,-7.2,-6.9,-6.6,-6.3,-6.0,-5.7,
-5.4,-5.1};
for(double d : a){
if(validate(d,-10.5,.3,-5))
System.err.println(d + " was considered valid.");
}
for(double d : b){
if(!validate(d, -10.5,.3,-5))
System.err.println(d + " was considered invalid");
}
/*
* Range
* min 2
* step .05
* max 3
*/
//Invalid values
double[] c = {1.09,2.055,2.06,2.14,2.16,2.56,2.97,3.05};
//Valid values
double[] e = {2.0,2.05,2.1,2.15,2.2,2.25,2.5,2.75,2.95,3.0};
for(double d : c){
if(validate(d,2,.05,3))
System.err.println(d + " was considered valid.");
}
for(double d : e){
if(!validate(d,2,.05,3))
System.err.println(d + " was considered invalid.");
}
}
private static boolean
validate(double value, double min, double step, double max){
return value >= min &&
value <= max &&开发者_开发问答;
(value - min) % step == 0;
}
}
If value
follows the step rule, then (value - min)/step
should be an integer. You can therefore check how close it is to the nearest integer, and decide if the distance is significant or not.
double ratio = (value-min)/step;
double distance = Math.Abs(ratio - Math.Round(ratio,0));
return distance < treshold;
Apart from being not so elegant, and maybe being slow, adding step
continuously to get close to the number being checked will result in less accurate calculations because floating-point errors tend to accumulate.
I don't know Java well, but an algorithm to do this would be to take the ratio: (value-min)/step
, round it to the nearest integer n
, and then calculate v = min+step*n
. If v
and value
are "close enough", then you can mark value
as valid.
To test for "close enough" floating-point values, one should use a relative and an absolute tolerance. For example, the package fcmp implements a fairly good algorithm to compare floating-point values.
精彩评论