Проверка двойного диапазона значений и шага
-
20-09-2019 - |
Вопрос
Я пытаюсь построить алгоритм, который проверяет, что значение double является членом диапазона, определенного значениями min, max и step.Проблема заключается в проверке того, что значение подчиняется правилу шага.Для целых чисел это можно легко сделать:
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;
}
Однако это не работает для двойных значений.Я знаю, что это, по крайней мере частично, делается из соображений точности, и я попытался взломать решение, умножив все значения на очень большое число и преобразовав их в длинные значения.Однако это сработало не для всех значений, и при проверке остатка также не было допущено небольшого отклонения от 0.Кто-нибудь сталкивался с этой проблемой и придумал хорошее решение?Ниже приведен пример и тест с использованием нерабочего метода проверки.
Одним из способов сделать это было бы начать с минимального значения и постепенно увеличивать его, пока оно не станет равным или больше входного значения, но помимо того, что это уродливое решение, это может быть потенциальным узким местом в моем приложении, поэтому я действительно хочу избежать этого.
Я благодарен за любые указания...
С уважением / Хенрик
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;
}
}
Решение
Если value
следует правилу шага, затем (value - min)/step
должно быть целым числом.Таким образом, вы можете проверить, насколько близко оно находится к ближайшему целому числу, и решить, является ли расстояние значительным или нет.
double ratio = (value-min)/step;
double distance = Math.Abs(ratio - Math.Round(ratio,0));
return distance < treshold;
Другие советы
Помимо того, что это не так элегантно и, возможно, медленно, добавляя step
постоянное приближение к проверяемому числу приведет к менее точным вычислениям, поскольку ошибки с плавающей запятой имеют тенденцию накапливаться.
Я не очень хорошо знаю Java, но алгоритм для этого состоял бы в том, чтобы взять соотношение: (value-min)/step
, округлить его до ближайшего целого числа n
, а затем вычислить v = min+step*n
.Если v
и value
находятся "достаточно близко", тогда вы можете отметить value
как действительный.
Чтобы проверить наличие "достаточно близких" значений с плавающей запятой, следует использовать относительный и абсолютный допуск.Например, пакет фкмп реализует довольно хороший алгоритм для сравнения значений с плавающей запятой.