Подтвердить что ты не робот

Почему сравнение совпадений float в Java?

class Test{  
    public static void main(String[] args){  
        float f1=3.2f;  
        float f2=6.5f;  

        if(f1==3.2){
            System.out.println("same");  
        }else{
            System.out.println("different");  
        }
        if(f2==6.5){
            System.out.println("same");  
        }else{  
            System.out.println("different");  
        }
    }  
}  

выход:

different
same

Почему это так? Я ожидал same как результат в первом случае.

4b9b3361

Ответ 1

Разница в том, что 6.5 можно представить точно как в float, так и в double - тогда как 3.2 не может быть точно представлена ​​в любом типе... и два ближайших приближения различны. Сравнение равенства между float и double сначала преобразует float в double, а затем сравнивает два. Таким образом, потеря данных.


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

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

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

Отметьте Что каждый компьютерный ученый должен знать о арифметике с плавающей точкой больше!

Ответ 2

Они представляют собой реализацию различных частей стандарта IEEE с плавающей запятой. A float имеет ширину 4 байта, а double - 8 байтов.

Как правило, вы, вероятно, предпочтете использовать double в большинстве случаев и использовать только float, когда у вас есть веские основания. (Примером хорошей причины использовать float в отличие от double является "Я знаю, что мне не нужна такая точность, и мне нужно хранить миллион из них в памяти".) Также стоит отметить, что трудно доказать, что вам не нужна точность double.

Кроме того, при сравнении значений с плавающей запятой для равенства вы обычно хотите использовать что-то вроде Math.abs(a-b) < EPSILON, где a и b - сравниваемые значения с плавающей запятой, а EPSILON - это небольшое значение с плавающей запятой как 1e-5. Причиной этого является то, что значения с плавающей запятой редко кодируют точное значение, которое они должны "- скорее, они обычно кодируют значение очень близко - так что вам нужно" прищуриться ", когда вы определяете, совпадают ли два значения.

РЕДАКТИРОВАТЬ. Все должны прочитать ссылку @Kugathasan Abimaran, размещенную ниже: Что каждый компьютерный ученый должен знать о арифметике с плавающей точкой для больше!

Ответ 3

Чтобы узнать, с чем вы имеете дело, вы можете использовать Float и Double метод toHexString:

class Test {
    public static void main(String[] args) {
        System.out.println("3.2F is: "+Float.toHexString(3.2F));
        System.out.println("3.2  is: "+Double.toHexString(3.2));
        System.out.println("6.5F is: "+Float.toHexString(6.5F));
        System.out.println("6.5  is: "+Double.toHexString(6.5));
    }
}
$ java Test
3.2F is: 0x1.99999ap1
3.2  is: 0x1.999999999999ap1
6.5F is: 0x1.ap2
6.5  is: 0x1.ap2

Как правило, число имеет точное представление, если оно равно A * 2 ^ B, где A и B - целые числа, допустимые значения которых задаются спецификацией языка (а double имеет более допустимые значения).

В этом случае,
6.5 = 13/2 = (1 + 10/16) * 4 = (1 + a/16) * 2 ^ 2 == 0x1.ap2, а 3.2 = 16/5 = (1 + 9/16 + 9/16 ^ 2 + 9/16 ^ 3 +...) * 2 ^ 1 == 0x1.999., p1.
Но Java может содержать только конечное число цифр, поэтому он сокращает 0,999., в какой-то момент. (Вы можете вспомнить из математики, что 0.999... = 1. Это в базе 10. В основании 16 это будет 0.fff.... = 1.)

Ответ 4

class Test {  
  public static void main(String[] args) {  
    float f1=3.2f;  
    float f2=6.5f;  

    if(f1==3.2f)  
      System.out.println("same");  
    else  
      System.out.println("different");  

    if(f2==6.5f)  
      System.out.println("same");  
    else  
      System.out.println("different");  
    }  
  }

Попробуйте это, и он будет работать. Без "f" вы сравниваете плавающий с другим плавающим типом и различной точностью, что может вызвать неожиданный результат, как в вашем случае.

Ответ 5

Невозможно напрямую сравнивать значения типа float и double. Перед сравнением значений необходимо либо преобразовать double в float, либо преобразовать float в double. Если сделать первое сравнение, преобразование спросит: "Имеет ли float наилучшее представление float значения double?" Если сделать последнее преобразование, вопрос будет: "Имеет ли float идеальное представление значения double". Во многих контекстах первый вопрос является более значимым, но Java предполагает, что все сравнения между float и double предназначены для того, чтобы задать последний вопрос.

Я бы предположил, что независимо от того, какой язык готов терпеть, одни стандарты кодирования должны абсолютно положительно запрещать прямые сравнения между операндами типа float и double. С учетом кода:

float f = function1();
double d = function2();
...
if (d==f) ...

невозможно определить, какое поведение предполагается в случаях, когда d представляет значение, которое не является точно представимым в float. Если предполагается, что f преобразуется в double, и результат этого преобразования по сравнению с d, следует написать сравнение как

if (d==(double)f) ...

Хотя приказ не меняет поведение кода, он ясно показывает, что поведение кода является преднамеренным. Если бы было намерение, что сравнение показывает, имеет ли f лучшее float представление d, оно должно быть:

if ((float)d==f)

Обратите внимание, что поведение этого очень сильно отличается от того, что произойдет без трансляции. Если бы ваш исходный код применял операнд double каждого сравнения к float, тогда оба теста равенства прошли бы.

Ответ 6

В целом не рекомендуется использовать оператор == с числом с плавающей запятой из-за проблем аппроксимации.

Ответ 7

6.5 можно представить точно в двоичном формате, тогда как 3.2 не может. Поэтому разница в точности не имеет значения для 6.5, поэтому 6.5 == 6.5f.

Чтобы быстро обновить, как работают двоичные числа:

100 -> 4

10 -> 2

1 -> 1

0.1 -> 0.5 (or 1/2)

0.01 -> 0.25 (or 1/4)

и др.

6.5 в двоичном формате: 110.1 (точный результат, остальные цифры равны нулю)

3.2 в двоичном формате: 11.001100110011001100110011001100110011001100110011001101... (здесь точность имеет значение!)

Поплавок имеет только 24-битную точность (остальные используются для знака и экспонента), поэтому:

3.2f в двоичном формате: 11.0011001100110011001100 (не равное аппроксимации двойной точности)

В основном это то же самое, что при написании 1/5 и 1/7 в десятичных числах:

1/5 = 0,2
1,7 = 0,14285714285714285714285714285714.

Ответ 8

Float имеет меньшую точность, чем double, bcoz float использует 32 бит, в которых 1 используется для Sign, 23 точности и 8 для Exponent. Если в качестве двойного используется 64 бита, в которых 52 используется для точности, 11 для показателя и 1 для знака.... Точность важна. Десятичное число, представленное как float и double, может быть равно или неравномерно зависит от необходимости точности (т.е. Диапазона чисел после десятичной точки может меняться). С уважением С. ЗАКИР