Valide o intervalo de valor duplo e a etapa
-
20-09-2019 - |
Pergunta
Estou tentando construir um algoritmo que valida que um valor duplo é um membro de um intervalo definido com valores de min, max e etapa. O problema é verificar se o valor obedece à regra da etapa. Para números inteiros, isso pode ser feito facilmente:
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;
}
No entanto, isso não funciona para valores duplos. Sei que isso pelo menos em parte é por razões de precisão e tentei invadir uma solução multiplicando todos os valores por um número muito alto e converti -os em longos. Isso não funcionou para todos os valores, e nem permitiu um pequeno desvio de 0 ao verificar o restante. Alguém teve esse problema e apresentou uma boa solução? Abaixo está um exemplo e teste com o método de validação que não funciona.
Um método de fazer isso seria começar pelo valor mínimo e aumentá -lo por etapa até que seja igual ou superior ao valor de entrada, mas além de ser uma solução feia, isso pode ser um gargalo em potencial no meu aplicativo, então eu realmente quero evitar isso.
Sou grato por quaisquer indicadores ...
Atenciosamente / 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;
}
}
Solução
Se value
segue a regra de etapa, então (value - min)/step
deve ser um número inteiro. Portanto, você pode verificar o quão próximo está do número inteiro mais próximo e decidir se a distância é significativa ou não.
double ratio = (value-min)/step;
double distance = Math.Abs(ratio - Math.Round(ratio,0));
return distance < treshold;
Outras dicas
Além de não ser tão elegante e talvez ser lento, acrescentando step
Continuamente para se aproximar do número verificado resultará em cálculos menos precisos, porque os erros de ponto flutuante tendem a se acumular.
Não conheço bem o Java, mas um algoritmo para fazer isso seria tomar a proporção: (value-min)/step
, arredondá -lo para o número inteiro mais próximo n
, e depois calcular v = min+step*n
. Se v
e value
estão "perto o suficiente", então você pode marcar value
como válido.
Para testar os valores de ponto flutuante "próximos o suficiente", deve-se usar um parente e uma tolerância absoluta. Por exemplo, o pacote fcmp implementa um algoritmo bastante bom para comparar valores de ponto flutuante.