2009-12-22 10 views
1

J'essaie de construire un algorithme qui valide qu'une valeur double est un membre d'une plage définie avec des valeurs min, max et step. Le problème est de vérifier que la valeur obéit à la règle de l'étape. Pour les entiers, cela peut se faire facilement:Valider la plage de valeurs doubles et l'étape

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

Cela ne fonctionne pas pour les valeurs doubles. Je sais que c'est au moins en partie pour des raisons de précision et j'ai essayé de hacker une solution en multipliant toutes les valeurs par un nombre très élevé et en les convertissant en longues. Cela n'a pas fonctionné pour toutes les valeurs, et n'a pas non plus permis une petite déviance de 0 en vérifiant le reste. Quelqu'un at-il eu ce problème et trouver une bonne solution? Voici un exemple et un test avec la méthode de validation non-travail. Une méthode consiste à commencer à la valeur min et à l'incrémenter étape par étape jusqu'à ce qu'elle soit égale ou supérieure à la valeur d'entrée, mais en plus d'être une solution laide, cela pourrait être un goulot d'étranglement potentiel dans mon app, donc je veux vraiment l'éviter.

Je suis reconnaissant pour tous les pointeurs ...

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

}

+0

Questions: - Doit les pas être exactement égale ou peuvent-ils varier? - Dans le premier cas: Cela vous dérange-t-il d'avoir 0.299995 au lieu de 0.3? Quelles sont les conditions? Parce que la condition « une double valeur est un membre d'une plage définie par min, max et les valeurs de l'étape » est pas possible avec chaque valeur double et la taille de pas! –

Répondre

3

Si value suit la règle de l'étape, alors (value - min)/step doit être un entier. Vous pouvez donc vérifier à quel point il est proche de l'entier le plus proche, et décider si la distance est significative ou non.

double ratio = (value-min)/step; 
double distance = Math.Abs(ratio - Math.Round(ratio,0)); 
return distance < treshold; 
+0

Cela a résolu mon problème et a travaillé avec tous mes tests. Mais si quelqu'un connaît un moyen de choisir un seuil optimal, merci de le partager. Je devine seulement ici ...;) Merci! – Bulgur

+0

Voir http://www.ibm.com/developerworks/java/library/j-math2.html, en particulier 'Math.ulp()'. Ainsi, vous pouvez décider combien de différence ULP est assez grande pour vous, et l'utiliser pour la tolérance. –

1

En plus d'être pas si élégant, et d'être peut-être lent, ajoutant step continue à se rapprocher du numéro en cours de vérification se traduira par des calculs moins précis, car les erreurs à virgule flottante ont tendance à accumuler.

Je ne connais pas bien Java, mais un algorithme pour ce faire serait de prendre le ratio: (value-min)/step, arrondi à l'entier le plus proche n, puis de calculer v = min+step*n. Si v et value sont "assez proches", vous pouvez marquer value comme valide.

Pour tester des valeurs à virgule flottante «assez proches», il faut utiliser une tolérance relative et une tolérance absolue. Par exemple, le package fcmp implémente un assez bon algorithme pour comparer les valeurs à virgule flottante.

Questions connexes