поплавок и двойная путаница

Я смущен поведением числа с плавающей запятой, увидев результат следующего фрагмента кода.

    float var1 = 5.4f;
    float var2 = 5.5f;

    if(var1 == 5.4)
        System.out.println("Matched");
    else
        System.out.println("Oops!!");

    if(var2 == 5.5)
        System.out.println("Matched");
    else
        System.out.println("Oops!!");

Вывод:

Oops!!
Matched

Это из-за того, что десятичное число не может быть точно представлено в двоичном формате с основанием 2? ИЛИ Это из-за точности, поскольку я сравниваю переменную типа float с типом double? Если да, то почему он отлично работает для следующей переменной?


person Imtiaz Zaman Nishith    schedule 19.09.2013    source источник
comment
Информации много, просто погуглите :) Во-первых, используйте double over float, а также посмотрите _1 _...   -  person vikingsteve    schedule 19.09.2013
comment
возможный дубликат Сравнение примитивов float и double в Java   -  person devnull    schedule 19.09.2013
comment
В другом вопросе это очень хорошо обсуждается stackoverflow.com/questions/1088216/   -  person AurA    schedule 19.09.2013


Ответы (5)


Это из-за того, что десятичное число не может быть точно представлено в двоичном формате с основанием 2?

да. По сути, 5.4f и 5.4d не одинаковы, потому что ни один из них не является точным представлением 5.4.

5.5f и 5.5d одинаковы, потому что они оба являются точным представлением 5.5.

Обратите внимание, что 5.4 неявно совпадает с 5.4d - типом по умолчанию для литерала с плавающей запятой является double. Любое использование бинарного оператора с операндами float и double повысит float до double и выполнит операцию с двумя значениями double.

Это может упростить представление об этом в терминах десятичных типов. Предположим, у нас есть два типа, Decimal5 и Decimal10, которые представляют собой десятичные числа с 5 или 10 значащими цифрами. Затем рассмотрим «треть» и «четверть»:

A third:
Decimal5:  0.33333
Decimal10: 0.3333333333

A quarter (showing trailing zeroes just for clarity):
Decimal5:  0.25000
Decimal10: 0.2500000000

При сравнении значения Decimal5, ближайшего к третьему, со значением Decimal10, значение Decimal5 будет преобразовано в значение Decimal10, равное 0,3333300000, что не равно 0,3333333333. Это похоже на ваш первый пример.

Однако при сравнении значений для квартала значение Decimal5 0,25000 преобразуется в 0,2500000000, что совпадает со значением Decimal10, которое мы имеем для квартала. Это похоже на ваш второй пример.

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

person Jon Skeet    schedule 19.09.2013
comment
Фактически, есть еще один момент, который не обсуждается. когда вы делаете if (var1 == 5.4), что на самом деле происходит? Похоже, что var1 неявно приводится к удвоению по сравнению с 5.4d. - person thang; 19.09.2013
comment
@thang: Да - я как бы обсуждал это в своей аналогии, показывая, что значение Decimal5 преобразуется в значение Decimal10. - person Jon Skeet; 19.09.2013
comment
Ну, есть кое-что конкретное, к чему я обращаюсь, что находится в if (var1 == 5.4), что происходит? это специфично для того, как java обрабатывает 5.4. почему он не приводит к неявному переводу 5.4? ваша аналогия просто демонстрирует, что 5.4f и 5.4d не одно и то же, но почему 5.4f и 5.4d не являются одной и той же причиной, что if ложно. - person thang; 19.09.2013
comment
@thang: потому что это литерал без f, что означает, что это неявно double. Я отредактировал это в ответе. - person Jon Skeet; 19.09.2013
comment
Да, знаю. я пытался убедить вас вставить это в ответ, чтобы следующий человек, который это прочитает, мог понять. круто хоть. Я бы также добавил, что он автоматически увеличивает переменную типа float var1 до double. - person thang; 19.09.2013
comment
@thang: Верно, добавил немного о продвижении. - person Jon Skeet; 19.09.2013

Разница в том, что 5.5 может быть точно представлена ​​как в float, так и в double, тогда как 5.4 не может быть представлена ​​точно.
ссылка http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

person Prabhaker A    schedule 19.09.2013

Замените условие if на:

if(var1 == 5.4f)
    System.out.println("Matched");
else
    System.out.println("Oops!!");

if(var2 == 5.5f)
    System.out.println("Matched");
else
    System.out.println("Oops!!");

Затем он будет печатать Matched оба раза. Причина в том, что без квалификатора f в конце Java обрабатывает 5.4 как double, что не может быть представлено точно по сравнению с 5.5.

person anubhava    schedule 19.09.2013
comment
Из вопроса ясно, что спрашивающий знает разницу между добавлением завершающего f и оставлением его. Его вопрос заключался в том, почему одна часть поплавка равна своей двойной ответной части, а другая - нет. @Jon Skeet ответил правильно. - person Andromeda; 19.09.2013

Легко увидеть:

if(var1 == 5.4f)
    System.out.println("Matched");
else
    System.out.println("Oops!!");

if(var2 == 5.5f)
    System.out.println("Matched");
else
    System.out.println("Oops!!");

Вывод:

Matched
Matched

Двоичное представление 5.4f и 5.4d не совсем то же самое

person Rafi Kamal    schedule 19.09.2013
comment
Педантичность: двоичное представление 5.5f и 5.5d также не одно и то же (как если бы вы сравнивали память напрямую). Вы, вероятно, хотели сказать, что фактические числа, представленные двоичным представлением 5.4f и 5.4d, не совпадают. - person thang; 19.09.2013

Если вы хотите сравнить типы с плавающей запятой, вы должны использовать сравнение с эпсилоном. Здесь вы можете увидеть все способы, которые вы можете использовать для сравнения типов с плавающей запятой. .

person Darwing    schedule 19.09.2013