"-Weverything" приводит к "Сравнение с плавающей запятой с == или!= Небезопасно"
У меня есть строка, которую я преобразую в двойной, как это:
double d = [string doubleValue];
Документация для doubleValue
говорит нам, что при переполнении этот метод возвращает либо HUGE_VAL
или же -HUGE_VAL
, Вот как я проверял это ранее:
if (d == HUGE_VAL || d == -HUGE_VAL)
//overflow
Теперь, после добавления нового флага предупреждения "-Weverything", компилятор теперь жалуется, что
Comparing floating point with == or != is unsafe
Как я могу решить эту проблему? Как я должен делать эти сравнения?
У меня также есть тот же вопрос о сравнении двух "нормальных" чисел с плавающей точкой (т.е. не "HUGE_VAL"). Например,
double a, b;
//...
if (a != b) //this will now yield the same warning
//...
Как это должно быть решено?
4 ответа
Вам не нужно беспокоиться об этом предупреждении. Это глупость во многих случаях, в том числе и в вашем.
Документация doubleValue
не говорит, что он возвращает что-то достаточно близкое к HUGE_VAL
или же -HUGE_VAL
на переполнении. Он говорит, что возвращает именно эти значения в случае переполнения.
Другими словами, значение, возвращаемое методом в случае переполнения, сравнивается ==
в HUGE_VAL
или же -HUGE_VAL
,
Почему предупреждение существует в первую очередь?
Рассмотрим пример 0.3 + 0.4 == 0.7
, Этот пример оценивается как ложный. Люди, включая авторов встреченного вами предупреждения, считают, что с плавающей точкой ==
неточно, и что неожиданный результат исходит из этой неточности.
Они все не правы.
Сложение с плавающей точкой является "неточным", в некотором смысле неточным: оно возвращает ближайшее представимое число с плавающей точкой для запрошенной вами операции. В приведенном выше примере преобразования (из десятичной в плавающую) и сложения с плавающей точкой являются причинами странного поведения.
Равенство с плавающей точкой, с другой стороны, работает почти так же, как и для других дискретных типов. Равенство с плавающей точкой является точным: за исключением незначительных исключений (значение NaN и случай +0. И -0.), Равенство оценивается как истинное, если и только если два рассматриваемых числа с плавающей точкой имеют одинаковое представление.
Вам не нужен эпсилон, чтобы проверить, равны ли два значения с плавающей точкой. И, как говорит Дьюар по существу, предупреждение в примере 0.3 + 0.4 == 0.7
должен быть на +
, не на ==
, чтобы предупреждение имело смысл.
Наконец, сравнение с эпсилоном означает, что значения, которые не равны, будут выглядеть одинаково, что не подходит для всех алгоритмов.
Если вы уверены в своем сравнении и хотите, чтобы он перезвонил, окружите свой код:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wfloat-equal"
/* My code triggering the warnings */
#pragma clang diagnostic pop
Возможно, это не обязательно для этого очень простого варианта использования, но вот пример того, как вы могли бы учесть потенциальное несоответствие, если бы вы пытались проверить, равно ли число -1:
#include <float.h>
#include <math.h>
int main(void) {
float sales = -1;
// basically if sales == -1
if (fabs(1 + sales) < FLT_EPSILON) {
return 0;
}
}
Поплавки не следует сравнивать с == или!= Из-за неточности типа с плавающей запятой, что может привести к непредвиденным ошибкам при использовании этих операторов. Вместо этого вы должны проверить, находятся ли поплавки на расстоянии друг от друга (чаще всего это называется "Эпсилон").
Это может выглядеть так:
const float EPSILON = 1.0f; // use a really small number instead of this
bool closeEnough( float f1, float f2)
{
return fabs(f1-f2)<EPSILON;
// test if the floats are so close together that they can be considered equal
}