Как сравнить, что последовательность удвоений все "приблизительно равна" в 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)

чтобы сравнить пять удвоений.