# V550. Suspicious precise comparison. Consider using a comparison with defined precision: fabs(A - B) < Epsilon or fabs(A - B) > Epsilon.

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

Рассмотрим пример:

    double a = 0.5;
    if (a == 0.5)               //OK
      x++;

    double b = sin(M_PI / 6.0);
    if (b == 0.5)               //ERROR
      x++;

Первое сравнение 'a == 0.5' истинно. Второе сравнение 'b == 0.5' может быть как истинно, так и ложно. Результат выражения 'b == 0.5' зависит от используемого процессора, версии и настроек компилятора. Например, значение переменной 'b' было равно 0.49999999999999994 при использовании компилятора Visual C++ 2010. Более корректно этот код можно написать следующим образом:

    double b = sin(M_PI / 6.0);
    if (fabs(b - 0.5) < DBL_EPSILON)
      x++;

В данном случае сравнение с погрешностью DBL_EPSILON верно, так как результат функции sin() лежит в диапазоне [-1, 1]. Однако, если мы работаем со значениями больше нескольких единиц, то такие погрешности как FLT_EPSILON, DBL_EPSILON будут слишком малы. И наоборот при работе со значениями типа 0.00001 эти погрешности слишком велики. Каждый раз следует выбирать погрешность адекватную диапазону возможных значений.

Возникает вопрос. Как же все-таки сравнить две переменных типа double?

    double a = ...;
    double b = ...;
    if (a == b) // how?
    {
    }

Одного единственно-правильного ответа нет. В большинстве случаев можно сравнит две переменных типа double, написав код следующего вида:

    if (fabs(a-b) <= DBL_EPSILON * fmax(fabs(a), fabs(b)))
    {
    }

Только осторожней с этой формулой, она работает только для чисел с одинаковым знаком. Также в ряде с большим количеством вычислений постоянно набегает ошибка, и константа 'DBL_EPSILON' может оказаться слишком маленьким значением.

А можно ли все-таки точно сравнивать значения в формате с плавающей точкой?

В некоторых случаях да. Но эти ситуации весьма ограничены. Сравнивать можно в том случае, если это и есть, по сути, одно и то же значение.

Пример, где допустимо точное сравнение:

    // -1 - признак, что значение переменной не было установлено
    float val = -1.0f;
    if (Foo1())
      val = 123.0f;
    if (val == -1.0f) //OK
    {
    }

В данном случае сравнение со значением "-1" допустимо, так как именно точно таким же значением мы инициализировали переменную ранее.

В рамках документации невозможно более подробно раскрыть тему сравнения float/double типов, поэтому мы отсылаем вас к внешнем источникам информации приведенных в конце.

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

Диагностическое сообщение не выдается, если сравниваются два идентичных выражения типа float или double. Такое сравнение позволяет определить, является ли значение NaN. Пример кода, реализующего подобную проверку:

    bool isnan(double X) { return X != X; }

Впрочем, такой код нельзя назвать хорошим и лучше использовать стандартную функцию 'std::isnan'.

Дополнительные ресурсы:

- Bruce Dawson. Comparing floating point numbers, 2012 Edition.
- RSDN. Про сравнение double (RU).
- Андрей Карпов. 64-битные программы и вычисления с плавающей точкой.
- Wikipedia. Floating point.
- CodeGuru Forums. C++ General: How is floating point representated?
- Boost. Floating-point comparison algorithms.

Данная диагностика классифицируется как:

CWE-682 CERT-FLP02-C

Взгляните на примеры ошибок, обнаруженных с помощью диагностики V550.