Как сравнить, что последовательность удвоений все "приблизительно равна" в Java?
У меня есть метод в java, который возвращает двойной номер, и я хочу сравнить каждый двойной номер, который возвращается каждый раз, когда я вызываю метод (скажем, 5 раз), так что я могу заключить, что возвращаемое число почти одинаково каждый раз.
Как я могу это сделать?
Ответы
Ответ 1
Вы должны сначала решить, что означает "почти то же самое". Например, существует метод в java.lang.Math
, называемый ulp(), который, учитывая двойной, возвращает расстояние между этим двойным и следующим; то есть наименьшую возможную разницу между этим числом и любым другим. Вы можете просто сравнить разницу между двумя удвоениями и результатом вызова этого метода.
С другой стороны, возможно, вы хотите, чтобы два числа находились в пределах 1% друг от друга. В этом случае выполните одно и то же вычисление, но используйте первое число, умноженное на 0.01
вместо ulp()
как наибольшее допустимое расстояние.
Ответ 2
public static boolean almostEqual(double a, double b, double eps){
return Math.abs(a-b)<eps;
}
Где eps - показатель равенства.
Ответ 3
Приближенное равенство определяется в терминах абсолютной разности: если абсолютная разница не превышает некоторого, предположительно малого, числа, то вы можете сказать, что сравниваемые значения "достаточно близки".
double diff = Math.abs(actual - expected);
if (diff < 1E-7) {
// Numbers are close enough
}
Вы должны быть очень осторожны, чтобы не путать "достаточно близкий" конец "равно", потому что эти два принципиально отличаются: равенство транзитивно (т.е. a == b и b == c вместе подразумевают, что a == c), в то время как "достаточно близко" не является транзитивным.
Ответ 4
Это зависит от того, что вы подразумеваете под похожим. Если вы хотите сравнить два числа в пределах абсолютной ошибки, например. 1e-6 вы можете использовать epsilon. Если вы хотите сравнить два double
независимо от масштаба. например 1.1e-20 и 1.3e-20 не похожи, но 1.1e20 и 1.1e20 + 1e5 вы можете сравнить исходное значение.
public static void main(String... args) throws IOException {
test(1.1e-20, 1.3e-20);
test(1.1e20, 1.1e20 + 1e5);
}
private static void test(double a, double b) {
System.out.println(a + " and " + b + ", similar= " + similarUnscaled(a, b, 10));
}
public static boolean similarUnscaled(double a, double b, long representationDifference) {
long a2 = Double.doubleToRawLongBits(a);
long b2 = Double.doubleToRawLongBits(b);
// avoid overflow in a2 - b2
return ((a2 >= 0) == (b2 >= 0)) &&
Math.abs(a2 - b2) <= representationDifference;
}
печатает
1.1E-20 and 1.3E-20, similar= false
1.1E20 and 1.100000000000001E20, similar= true
Ответ 5
Что означает, что для двух двухлокальных "примерно равных"? Это означает, что двойники находятся в пределах некоторой толерантности друг к другу. Размер этого допуска и то, что этот толерант выражается как абсолютное число или процент от двух удвоений, зависит от вашего приложения.
Например, две фотографии, отображаемые в средстве просмотра фотографий, имеют примерно такую же ширину в дюймах, если они занимают одинаковое количество пикселей на экране, поэтому ваш допуск будет абсолютным числом, рассчитанным на основе размера пикселя для вашего экрана. С другой стороны, прибыль двух финансовых фирм, вероятно, "приблизительно равна", если они находятся в пределах 0,1% друг от друга. Это всего лишь гипотетические примеры, но дело в том, что это зависит от вашего приложения.
Теперь для некоторой реализации. Скажем, ваше приложение требует абсолютного допуска. Затем вы можете использовать
private static final double TOLERANCE = 0.00001;
public static boolean approxEqual(final double d1, final double d2) {
return Math.abs(d1 - d2) < TOLERANCE;
}
сравнить два удвоения и использовать
approxEqual(d1, d2) && approxEqual(d1, d3) && approxEqual(d1, d4) && approxEqual(d1, d5)
чтобы сравнить пять удвоений.