Рубрики
Без рубрики

Сравнение двойников в Java

В этом уроке мы поговорим о различных способах сравнения двойных значений в Java.

Автор оригинала: baeldung.

1. Обзор

В этом уроке мы поговорим о различных способах сравнения двойных значений в Java. В частности, это не так просто, как сравнение других примитивных типов. На самом деле, это проблематично во многих других языках, а не только в Java.

Во-первых, мы объясним, почему использование простого является неточным и может привести к трудностям отслеживания ошибок во время выполнения. Затем мы покажем, как правильно сравнивать двойники в простой Java и распространенных сторонних библиотеках.

2. Использование

Неточность при сравнении с использованием метода вызвана тем, как двойные значения хранятся в памяти компьютера. Мы должны помнить, что существует бесконечное количество значений, которые должны поместиться в ограниченном пространстве памяти, обычно 64 бита. В результате мы не можем иметь точное представление большинства двойных значений в наших компьютерах . Они должны быть округлены, чтобы быть сохранены .

Из-за неточности округления могут возникнуть интересные ошибки:

double d1 = 0;
for (int i = 1; i <= 8; i++) {
    d1 += 0.1;
 }

double d2 = 0.1 * 8;

System.out.println(d1);
System.out.println(d2);

Обе переменные, d1 и d2, должны быть равны 0,8. Однако, когда мы запустим приведенный выше код, мы увидим следующие результаты:

0.7999999999999999
0.8

В этом случае сравнение обоих значений с результатом приведет к неправильному результату. По этой причине мы должны использовать более сложный алгоритм сравнения.

Если мы хотим иметь наилучшую точность и контроль над механизмом округления, мы можем использовать класс java.math.BigDecimal.

3. Сравнение двойников в простой Java

Рекомендуемым алгоритмом для сравнения двойных значений в простой Java является метод порогового сравнения . В этом случае нам нужно проверить, находится ли разница между обоими числами в пределах указанного допуска, обычно называемого эпсилон :

double epsilon = 0.000001d;

assertThat(Math.abs(d1 - d2) < epsilon).isTrue();

Чем меньше значение эпсилона, тем выше точность сравнения. Однако, если мы зададим слишком малое значение допуска, мы получим тот же ложный результат, что и в простом. В общем, значение эпсилона с 5 и 6 десятичными знаками обычно является хорошим местом для начала .

К сожалению, в стандартном JDK нет утилиты, которую мы могли бы использовать для сравнения двойных значений рекомендуемым и точным способом. К счастью, нам не нужно писать его самим. Мы можем использовать различные специальные методы, предоставляемые бесплатными и широко известными сторонними библиотеками.

4. Использование Apache Commons Math

Apache Commons Math -одна из крупнейших библиотек с открытым исходным кодом, посвященных компонентам математики и статистики. Из множества различных классов и методов мы сосредоточимся на org.apache.commons.math3.util.Точность класс в частности. Он содержит 2 полезных equals() метода для правильного сравнения двойных значений :

double epsilon = 0.000001d;

assertThat(Precision.equals(d1, d2, epsilon)).isTrue();
assertThat(Precision.equals(d1, d2)).isTrue();

Переменная epsilon , используемая здесь, имеет то же значение, что и в предыдущем примере. Это величина допустимой абсолютной ошибки. Однако это не единственное сходство с пороговым алгоритмом. В частности, оба метода equals используют один и тот же подход под капотом.

Версия функции с двумя аргументами-это просто ярлык для вызова метода equals(d1, d2, 1)|/. Значение эпсилона в этой версии довольно высокое. Поэтому мы не должны использовать его и всегда указывать значение допуска самостоятельно.

5. Использование Гуавы

Google Guava – это большой набор основных библиотек Java, которые расширяют стандартные возможности JDK. Он содержит большое количество полезных математических элементов в пакете com.google.common.math . Чтобы правильно сравнить двойные значения в Guava, давайте реализуем метод fuzzyEquals() из класса Double Math |:

double epsilon = 0.000001d;

assertThat(DoubleMath.fuzzyEquals(d1, d2, epsilon)).isTrue();

Имя метода отличается от названия в математике Apache Commons, но под капотом он работает практически одинаково. Единственное отличие заключается в том, что нет перегруженного метода со значением по умолчанию epsilon.

6. Использование JUnit

JUnit является одним из наиболее широко используемых фреймворков модульного тестирования для Java. В общем, каждый модульный тест обычно заканчивается анализом разницы между ожидаемыми и фактическими значениями. Поэтому структура тестирования должна иметь правильные и точные алгоритмы сравнения. Фактически, JUnit предоставляет набор методов сравнения для общих объектов, коллекций и примитивных типов, включая специальные методы для проверки равенства двойных значений:

double epsilon = 0.000001d;
assertEquals(d1, d2, epsilon);

На самом деле, он работает так же, как методы Guava и Apache Commons, описанные ранее.

Важно отметить, что существует также устаревшая версия с двумя аргументами без аргумента epsilon. Однако, если мы хотим быть уверены, что наши результаты всегда верны, мы должны придерживаться версии с тремя аргументами.

7. Заключение

В этой статье мы рассмотрели различные способы сравнения двойных значений в Java.

Мы объяснили, почему простое сравнение может привести к трудностям отслеживания ошибок во время выполнения. Затем мы показали, как правильно сравнивать значения в обычной Java и общих библиотеках.

Как всегда, исходный код примеров можно найти на GitHub .