Сравнение одинаковых значений Float в C [дубликат]

Возможные Дубликаты:
странный выход по сравнению с float с float literal

когда я пытаюсь сравнить 2 одинаковые float значения он не печатает "равные значения" в следующем коде:

void main()
{
    float a = 0.7;
    clrscr();
    if (a < 0.7)
        printf("value :  %f",a);
    else if (a == 0.7)
        printf("equal values");
    else
        printf("hello");
    getch();
}

спасибо заранее.

7 ответов


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

код конкретные проблема вот в чем:

float a = 0.7;

использует двойной постоянный 0.7 создать один точности число (потери точности) при:

if (a == 0.7)

сравним два двойной точность чисел (a сначала продвигали).

точность, которая была потеряна при повороте double 0.7 в поплавковой a не восстанавливается при продвижении a обратно в дубль.

если вы измените все эти 0.7 значения 0.7f (заставить плавать, а не двойной), или если вы просто сделать a двойной, он будет работать нормально - я редко пользуюсь float в настоящее время, если у меня есть массив из них и нужно экономить пространство.

вы можете увидеть это в действии с:

#include <stdio.h>
int main (void){
    float f = 0.7;    // double converted to float
    double d1 = 0.7;  // double kept as double
    double d2 = f;    // float converted back to double

    printf ("double:            %.30f\n", d1);
    printf ("double from float: %.30f\n", d2);

    return 0;
}

который выведет что-то вроде (слегка измененное, чтобы показать разницу):

double:            0.6999999|99999999955591079014994
double from float: 0.6999999|88079071044921875000000
                            \_ different beyond here.

число с плавающей запятой не то, что вы думаете: вот два источника с дополнительной информацией:Что Каждый Компьютерщик Должен Знать Об Арифметике С Плавающей Запятой и Руководство С Плавающей Запятой.

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


вы сравниваете приближение с одной точностью 0,7 с приближением с двойной точностью. Чтобы получить ожидаемый результат, вы должны использовать:

if(a == 0.7f) // check a is exactly 0.7f

обратите внимание, что из-за ошибок представления и округления он может быть очень маловероятным, чтобы когда-либо получить точно 0.7 f от любой операции. В общем, вы должны проверить, если fabs(a-0.7) - Это достаточно близко к 0.

Не забывайте, что точное значение 0.7 f на самом деле не 0.7, но немного ниже:

0.7f = 0.699999988079071044921875

точное значение представления двойной точности 0,7 является лучшим приближением, но все же не совсем 0,7:

0.7d = 0.6999999999999999555910790149937383830547332763671875

a Это float; 0.7 - значение типа double.

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

но никогда не сравнивайте числа с плавающей точкой (float, double или long double) С ==.

вы могли бы прочитать "Что Каждый Программист Должен Знать Об Арифметике С Плавающей Запятой".


числа с плавающей запятой не должны сравниваться с оператором"==".

вместо сравнения чисел float с оператором "= = " вы можете использовать такую функцию :

 //compares if the float f1 is equal with f2 and returns 1 if true and 0 if false
 int compare_float(float f1, float f2)
 {
  float precision = 0.00001;
  if (((f1 - precision) < f2) && 
      ((f1 + precision) > f2))
   {
    return 1;
   }
  else
   {
    return 0;
   }
 }

отсутствие абсолютной точности в поплавках затрудняет тривиальные сравнения, чем для целых чисел. См.этой страница по сравнению поплавков в C. В частности, один фрагмент кода, поднятый оттуда, демонстрирует "обходной путь" к этой проблеме:

bool AlmostEqual2sComplement(float A, float B, int maxUlps)
{
    // Make sure maxUlps is non-negative and small enough that the
    // default NAN won't compare as equal to anything.
    assert(maxUlps > 0 && maxUlps < 4 * 1024 * 1024);
    int aInt = *(int*)&A;
    // Make aInt lexicographically ordered as a twos-complement int
    if (aInt < 0)
        aInt = 0x80000000 - aInt;
    // Make bInt lexicographically ordered as a twos-complement int
    int bInt = *(int*)&B;
    if (bInt < 0)
        bInt = 0x80000000 - bInt;
    int intDiff = abs(aInt - bInt);
    if (intDiff <= maxUlps)
        return true;
    return false;
}

простым и распространенным обходным путем является предоставление epsilon кода следующим образом:

if (fabs(result - expectedResult) < 0.00001)

это по существу проверяет разницу между значениями в пределах порога. Посмотреть связанная статья что касается того, почему это не всегда оптимально, хотя:)

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


Если вам нужно сравнить a С 0.7 чем

if( fabs(a-0.7) < 0.00001 )
  //your code

здесь 0.00001 можно изменить на меньше (например, 0.00000001) или больше (например, 0.0001) > это зависит от точности, которая вам нужна.