Как сравнить эту последовательность пар "все примерно равны" в Java? - PullRequest
20 голосов
/ 01 февраля 2012

У меня есть метод в Java, который возвращает двойное число, и я хочу сравнивать каждое двойное число, которое возвращается каждый раз, когда я вызываю метод (скажем, 5 раз), так что я могу заключить, что возвращаемое число почти одинаковокаждый раз.

Как я могу это сделать?

Ответы [ 6 ]

34 голосов
/ 01 февраля 2012
public static boolean almostEqual(double a, double b, double eps){
    return Math.abs(a-b)<eps;
}

Где eps - мера равенства.

8 голосов
/ 01 февраля 2012

Приблизительное равенство определяется в терминах абсолютной разницы: если абсолютная разница не превышает определенного, предположительно небольшого, числа, то вы можете сказать, что сравниваемые значения являются «достаточно близкими».

double diff = Math.abs(actual - expected);
if (diff < 1E-7) {
    // Numbers are close enough
}

Вы должны быть очень осторожны, чтобы не перепутать "достаточно близко" конец "равно", потому что два принципиально различны: равенство транзитивно (то есть a == b и b == c вместе означают, что a == c), в то время как «достаточно близко» не является переходным.

8 голосов
/ 01 февраля 2012

Вы должны сначала решить, что означает "почти то же самое".Например, в java.lang.Math есть метод с именем ulp () , который при заданном двойном возвращает расстояние между этим двойным и следующим;т.е. наименьшая возможная разница между этим числом и любым другим.Вы можете просто сравнить разницу между двумя значениями типа double и результат вызова этого метода.

С другой стороны, может быть, вы хотите, чтобы два числа находились в пределах 1% друг от друга.В этом случае выполните те же вычисления, но используйте первое число, умноженное на 0.01 вместо ulp() в качестве наибольшего допустимого расстояния.

4 голосов
/ 01 февраля 2012

Это зависит от того, что вы подразумеваете под подобным. Если вы хотите сравнить два числа с абсолютной ошибкой, например, 1e-6 вы можете использовать эпсилон. Если вы хотите сравнить два 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
2 голосов
/ 17 апреля 2018

Вы можете использовать метод Гуава и DoubleMath#fuzzyEquals (начиная с версии 13.0):

public static boolean fuzzyEquals(double a, double b, double tolerance)

Возвращает true, если a и b находятся в пределах допуска друг друга.Технически говоря, это эквивалентно Math.abs (a - b) <= допуск ||Double.valueOf (a) .equals (Double.valueOf (b)). </p>

Известные особые случаи включают:

Ссылка на документы: https://google.github.io/guava/releases/17.0/api/docs/com/google/common/math/DoubleMath.html

1 голос
/ 01 февраля 2012

Что значит для двух двойных быть «примерно равными»? Это означает, что двойники находятся в некоторой терпимости друг к другу. Размер этого допуска и то, выражается ли это отклонение в виде абсолютного числа или в процентах от двух двойных, зависит от вашего приложения.

Например, две фотографии, отображаемые в программе просмотра фотографий, имеют приблизительно одинаковую ширину в дюймах, если они занимают одинаковое количество пикселей на экране, поэтому ваш допуск будет абсолютным числом, рассчитанным на основе размера пикселя для вашего экрана. С другой стороны, прибыль двух финансовых фирм, вероятно, «приблизительно равна», если они находятся в пределах 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)

для сравнения пяти двойных.

...