Сравнение с плавающей точкой

int main()
{
    float a = 0.7;
    float b = 0.5;
    if (a < 0.7)
    {
       if (b < 0.5) printf("2 are right");
       else         printf("1 is right");
    }
    else printf("0 are right");
}

Я ожидал, что вывод этого кода будет 0 are right. Но, к моему сожалению, выход 1 is right почему?

Ответ 1

int main()
{
    float a = 0.7, b = 0.5; // These are FLOATS
    if(a < .7)              // This is a DOUBLE
    {
      if(b < .5)            // This is a DOUBLE
        printf("2 are right");
      else
        printf("1 is right");
    }
    else
      printf("0 are right");
}

Поплавки повышаются до удвоения во время сравнения, а так как поплавки менее точны, чем удваиваются, 0.7 в качестве float не совпадает с 0.7 как double. В этом случае 0,7 как float становится ниже 0,7 как удвоенный, когда он получает повышение. И, как сказал христианин, 0,5, являющийся степенью 2, всегда представляется точно, поэтому тест работает так, как ожидалось: 0.5 < 0.5 является ложным.

Итак, либо:

  • Измените float на double или:
  • Измените .7 и .5 на .7f и .5f,

и вы получите ожидаемое поведение.

Ответ 2

Проблема в том, что константы, которые вы сравниваете, double not float. Кроме того, изменение ваших констант на что-то представимое легко, например, коэффициент 5 заставит его сказать 0 is right. Например,

main()
{
    float a=0.25,b=0.5; 
    if(a<.25) 
       {
       if(b<.5) 
               printf("2 are right");
       else
               printf("1 is right");
       }
else
printf("0 are right");
}

Вывод:

0 are right

Этот вопрос SO на Самый эффективный способ для float и двойного сравнения охватывает эту тему.

Кроме того, эта статья в cygnus сравнение чисел с плавающей запятой дает нам несколько советов:

Плавающие и двойные форматы IEEE были сконструированы таким образом, чтобы номера "лексикографически упорядочены", что, по словам IEEE архитектор Уильям Кахан означает "если два числа с плавающей запятой в тот же формат упорядочен (скажем, x < y), тогда они упорядочиваются одинаково когда их биты переинтерпретируются как целые числа Sign-Magnitude."

Это означает, что если взять две поплавки в памяти, интерпретировать их бит шаблон как целые числа и сравнить их, мы можем сказать, что больше, без сравнения с плавающей запятой. На языке C/С++ это сравнение выглядит следующим образом:

if (*(int*)&f1 < *(int*)&f2)

Этот очаровательный синтаксис означает адрес f1, рассматривать его как целочисленный указатель и разыгрывать его. Все эти операции указателя выглядят дорогие, но они в основном все сбрасывают и просто означают "лечить f1 как целое число. Поскольку мы применяем тот же синтаксис к f2, вся строка означает" сравнить f1 и f2, используя их представления в памяти интерпретируются как целые числа вместо float.

Ответ 3

Это из-за проблем округления при преобразовании из float в double

Ответ 4

В целом сравнение равенства с поплавками - опасный бизнес (что эффективно, что вы делаете, когда вы сравниваете прямо на границе > ), помните, что в десятичной форме некоторые фракции (например, 1/3) не могут быть точно выражены, то же самое можно сказать о двоичном,

0.5= 0.1, будет одинаковым в float или double.

0.7=0.10110011001100 и т.д. навсегда, 0.7 не может быть точно представлен в двоичном формате, вы получаете ошибки округления и могут (очень сильно) отличаться между float и double

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

Ответ 5

Кроме того, btw, у вас есть ошибка в вашей логике 0, являются правильными. Вы не проверяете b, когда вы выводите 0, являются правильными. Но все это немного загадочно в том, что вы действительно пытаетесь достичь. Сравнение с плавающей точкой между поплавками и удвоениями будет иметь вариации, минута, поэтому вам следует сравнить с дельта-приемлемым вариантом для вашей ситуации. Я всегда делал это через встроенные функции, которые просто выполняли работу (делали это один раз с помощью макроса, но это слишком грязно). Во всяком случае, yah, округлые проблемы изобилуют этим примером. Прочитайте материал с плавающей запятой и знайте, что .7 отличается от .7f и присваивания .7 плавающей запятой приведет к двойному в float, тем самым изменяя точную природу значения. Но предположение о том, что b было неправильным, поскольку вы проверили меня, я должен был заметить, что:)