Правильное сравнение float или сравнение double — это проблема, специфичная не только для Java. Сегодня ее можно наблюдать практически во всех языках программирования. В памяти компьютера float и double хранятся в стандартном формате IEEE 754. Как работает фактическое хранение и преобразование, выходит за рамки этой статьи.
Пока просто поймите, что во время вычислений и преобразований в эти числа могут быть внесены незначительные ошибки округления. Вот почему не рекомендуется просто полагаться на операторы равенства(==) для сравнения чисел с плавающей точкой.
Давайте узнаем, как сравнивать значения с плавающей точкой в Java.
1. Сравнение двойных чисел – Простое сравнение [Не рекомендуется]
Сначала посмотрите на простое сравнение, чтобы понять, что именно не так со сравнением double с оператором ==. В данной программе я создаю одно и то же число с плавающей точкой(т. е. 1.1) двумя способами:
- Прибавьте 0,1 11 раз.
- Умножьте 0,1 на 11.
Теоретически обе операции должны дать число 1,1. И когда мы сравним результаты обоих методов, они должны совпасть.
private static void simpleFloatsComparison(){//Method 1double f1 = .0;for(int i = 1; i <= 11; i++) {f1 += .1;}//Method 2double f2 = .1 * 11;System.out.println("f1 = " + f1);System.out.println("f2 = " + f2);if(f1 == f2)System.out.println("f1 and f2 are equal\n");elseSystem.out.println("f1 and f2 are not equal\n");}
Вывод программы.
f1 = 1.0999999999999999f2 = 1.1f1 and f2 are not equal
Посмотрите на оба значения, выведенные в консоли. f1 вычисляется как 1.0999999999999999. Это как раз та проблема, которую округление вызывает изнутри. Вот почему сравнение с плавающей точкой с оператором ‘==’ не рекомендуется.
2. Сравнение дважды – сравнение на основе порогового значения [рекомендуется]
Теперь, когда мы знаем проблему с оператором равенства, давайте решим ее. Используя программирование, мы не можем изменить способ хранения или вычисления этих чисел с плавающей точкой. Поэтому нам нужно адаптировать решение, в котором мы соглашаемся, что a определяет разницу в обоих значениях, которую мы можем допустить и по-прежнему считать числа равными. Эта согласованная разница в значениях называется порогом или эпсилоном.
Итак, чтобы использовать «сравнение чисел с плавающей точкой на основе порогового значения», мы можем использовать метод Math.abs() для вычисления разницы между двумя числами и сравнения разницы с пороговым значением.
private static void thresholdBasedFloatsComparison(){final double THRESHOLD = .0001;//Method 1double f1 = .0;for(int i = 1; i <= 11; i++) {f1 += .1;}//Method 2double f2 = .1 * 11;System.out.println("f1 = " + f1);System.out.println("f2 = " + f2);if(Math.abs(f1 - f2) < THRESHOLD)System.out.println("f1 and f2 are equal using threshold\n");elseSystem.out.println("f1 and f2 are not equal using threshold\n");}
Вывод программы.
f1 = 1.0999999999999999f2 = 1.1f1 and f2 are equal using threshold
3. Сравнение чисел типа double – Сравнение с BigDecimal [Рекомендуется]
В классе BigDecimal вы можете указать режим округления и точную точность, которые вы хотите использовать. Используя предел точной точности, ошибки округления в основном решаются.
Самое лучшее то, что числа BigDecimal неизменяемы, то есть если вы создадите BigDecimal BD со значением «1.23», этот объект останется «1.23» и никогда не сможет быть изменен. Этот класс предоставляет множество методов, которые можно использовать для выполнения числовых операций над его значением.
Вы можете использовать его метод compareTo() для сравнения с числами BigDecimal. Он игнорирует масштаб при сравнении.
а.сравнитьС(б);
Метод возвращает:
-1 – если а < б)
0 – если а == б
1 – если а > б
Никогда не используйте метод equals() для сравнения экземпляров BigDecimal. Это потому, что эта функция equals будет сравнивать масштаб. Если масштаб отличается, equals() вернет false, даже если математически они являются одним и тем же числом.
Программа Java для сравнения чисел двойной точности с классом BigDecimal.
private static void testBdEquality(){BigDecimal a = new BigDecimal("2.00");BigDecimal b = new BigDecimal("2.0");System.out.println(a.equals(b)); // falseSystem.out.println(a.compareTo(b) == 0); // true}
Теперь, чтобы проверить, давайте решим исходную задачу, используя класс BigDecimal.
private static void bigDecimalComparison(){//Method 1BigDecimal f1 = new BigDecimal("0.0");BigDecimal pointOne = new BigDecimal("0.1");for(int i = 1; i <= 11; i++) {f1 = f1.add(pointOne);}//Method 2BigDecimal f2 = new BigDecimal("0.1");BigDecimal eleven = new BigDecimal("11");f2 = f2.multiply(eleven);System.out.println("f1 = " + f1);System.out.println("f2 = " + f2);if(f1.compareTo(f2) == 0)System.out.println("f1 and f2 are equal using BigDecimal\n");elseSystem.out.println("f1 and f2 are not equal using BigDecimal\n");}
Вывод программы.
f1 = 1.1f2 = 1.1f1 and f2 are equal using BigDecimal
Вот и все о сравнении чисел с плавающей точкой в Java. Поделитесь своими мыслями в разделе комментариев.