Можем ли мы полагаться на op == для двоичного сравнения значений с плавающей точкой? - PullRequest
14 голосов
/ 08 ноября 2011

Мы все знаем (верно ?!), что нельзя сравнивать значения с плавающей точкой, проверяя на равенство (operator==).

Но что, если я действительно хочу определить, являются ли два float s a и b двоичными равными ? Если им не разрешено быть NaN (или другими «особыми значениями»), это «безопасно»? Могу ли я рассчитывать на operator== для такой работы?

Ответы [ 3 ]

20 голосов
/ 08 ноября 2011

(Предполагая представления IEEE-754) почти, но не совсем. Если вы можете исключить NaN, вам все равно придется иметь дело с тем фактом, что +0.0 и -0.0 имеют разные двоичные кодировки, но сравнивают их одинаково (потому что оба точно равны нулю ).

Конечно, C ++ не требует IEEE-754. Строго говоря, все ставки сняты.

Если вы хотите проверить (не) равенство кодировки, просто используйте memcmp(&a, &b, sizeof a).

5 голосов
/ 08 ноября 2011

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

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

  void function( float a )
  {
     float b = a / 0.12345;
     assert( b == (a/0.12345) );
  }

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

Если вам нужна гарантия, вам нужно сделать функцию сравнения, которая принимает два параметра с плавающей запятой и гарантировать, что эта функция никогда не будет встроена (сохраненные значения с плавающей запятой не подлежат повышенной точности). То есть вы должны убедиться, что эти поплавки действительно сохранены. Существует также опция GCC под названием «store-float», которая, я полагаю, заставляет хранилище, возможно, его можно использовать здесь для вашей индивидуальной функции.

0 голосов
/ 08 ноября 2011

Чтобы убедиться, что значение с плавающей запятой не является NaN, вы можете сравнить его с самим собой:

double foo;
// do something with foo
if (foo != foo) {
    std::cout << "Halp! Foo is NaN!";
}

Я почти уверен, что это гарантируется стандартом IEEE-754.

...