Недавно я решал интересную ошибку, которая сводилась к сравнению двух двойных
переменных с помощью метода равно
. Это выглядит невинно, что может быть плохого в чем-то вроде first Double.equals(secondDouble)
? Проблема здесь заключается в том, как хранятся двойники. Чтобы уместить их в 64 байта (обычно), они округляются.
Смотрите пример ниже:
Double firstDouble = 0d; for (int i = 1; i <= 42; i++) { firstDouble += 0.1; } Double secondDouble = 0.1 * 42; System.out.println(firstDouble); // 4.200000000000001 System.out.println(secondDouble); // 4.2 System.out.println(firstDouble.equals(secondDouble)); // false
Эта неточность вызвана ошибками округления. Нам нужно использовать другой подход для сравнения этих двойников.
Пороговый способ
Если у нас нет доступа к каким-либо библиотекам и мы хотим решить эту проблему только с помощью Java, мы можем использовать нечто, называемое пороговым методом. Просто мы вычтем эти удвоения, получим абсолютное значение и сравним, будет ли результат меньше какого-то очень малого числа.
double epsilon = 0.000001d; boolean result = Math.abs(firstDouble - secondDouble) < epsilon; System.out.println(result); // true
Это небольшое число называется эпсилон, и чем оно меньше, тем выше точность результата. В большинстве случаев должно быть достаточно 5 знаков после запятой.
Математика Apache Commons
В JDK для этого нет служебного метода. К счастью для нас, математическая библиотека Apache Commons заботится о нас. С его помощью мы можем сравнить эти двойники следующим образом:
double epsilon = 0.000001d; boolean result = Precision.equals(firstDouble, secondDouble, epsilon) System.out.println(result);
Эпсилон имеет то же значение, что и в приведенном выше примере.
Аналогичные методы существуют в Guava и других библиотеках.
Вы можете следовать за мной по Твиттер чтобы получить больше подобных советов.
Оригинал: “https://dev.to/pavel_polivka/double-comparison-in-java-1b7”