Сравнение с плавающей точкой 0 - PullRequest
6 голосов
/ 12 октября 2010

Если foo относится к типу с плавающей запятой, допустимо / рекомендовано следующее выражение?

(0.0f == foo * float(0))

Будет ли оно иметь ожидаемое (математическое) значение независимо от значения foo?

Стандарт C ++ определяет поведение или это зависит от реализации?

Ответы [ 5 ]

3 голосов
/ 12 октября 2010

Ну, во-первых, это не вопрос стандарта C ++. Скорее всего, речь идет о стандарте модели с плавающей точкой (скорее всего, IEEE).

Для операций с плавающей запятой IEEE это, вероятно, безопасно, поскольку float(0) должно приводить к тому же числу, что и 0.0f, а значение, умноженное на любое другое число, также должно быть 0.0f.

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

2 голосов
/ 12 октября 2010

NaN и Infinites могут испортить такие сравнения, как уже упоминали другие.

Однако есть еще один подводный камень: в C ++ нельзя полагаться на выражение времени компиляции типа float, сравнивая его с тем же выражением, вычисленным во время выполнения.

Причина этого в том, что C ++ допускает повышенную точность вычислений fp любым способом. Пример:

#include <iostream>

// This provides sufficent obfuscation so that g++ doesn't just inline results.
bool obfuscatedTrue() { return true; }

int main()
{
    using namespace std;

    double const    a   = (obfuscatedTrue()? 3.0 : 0.3);
    double const    b   = (obfuscatedTrue()? 7.0 : 0.7);
    double const    c   = a/b;

    cout << (c == a/b? "OK." : "\"Wrong\" comparision result.") << endl;
}

Результаты с одним конкретным компилятором:

C:\test> g++ --version | find "++"
g++ (TDM-2 mingw32) 4.4.1

C:\test> g++ fp_comparision_problem.cpp & a
"Wrong" comparision result.

C:\test> g++ -O fp_comparision_problem.cpp & a
OK.

C:\test> _

Приветствия & hth.,

- Альф

2 голосов
/ 12 октября 2010

AFAIK, это не обязательно, это может также оказаться очень близко к 0.

Обычно лучше сравнивать с эпсилоном. Я использую такую ​​функцию для таких сравнений:

float EpsilonEqual( float a, float b, float epsilon )
{
    return fabsf( a - b ) < epsilon;
}
1 голос
/ 12 октября 2010

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

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

Почему мои цифры, такие как 0,1 + 0,2, не суммируются с хорошим раундом 0,3, а вместо этого я получаю странный результат, такой как 0,30000000000000004?

Поскольку внутренне компьютеры используютформат (двоичная с плавающей точкой), который не может точно представить число, такое как 0,1, 0,2 или 0,3.

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

Чтение Руководство с плавающей запятой дляподробные объяснения и как правильно сравнивать с ожидаемыми значениями .

0 голосов
/ 12 октября 2010

Я только что прочитал эту статью в msdn о параметре / fp в VisualStudio текст ссылки

Оптимизация выражений, недопустимая для специальных значений (NaN, + infinity, -infinity, +0, -0) не будут разрешены.Оптимизации xx => 0, x * 0 => 0, x-0 => x, x + 0 => x и 0-x => -x недопустимы по разным причинам (см. IEEE 754 и стандарт C99).

...