views:

216

answers:

2

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;
}

}

+1  A: 

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;
Victor Nicollet
This solved my problem and worked with all of my tests. But should someone know a way of choosing an optimal threshold, please share. I'm only guessing here... ;)Thanks!
Bulgur
See http://www.ibm.com/developerworks/java/library/j-math2.html, particularly `Math.ulp()`. So, you can decide how many ULPs difference is big enough for you, and use that for tolerance.
Alok
A: 

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.

Alok