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

}

Foi útil?

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.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top