Сравнение чисел с плавающей точкой в ​​C - PullRequest
16 голосов
/ 10 августа 2011

У меня есть double, который печатается как 0.000000, и я пытаюсь сравнить его с 0.0f, но безуспешно. Почему здесь есть разница? Какой самый надежный способ определить, равен ли ваш дубль нулю?

Ответы [ 4 ]

20 голосов
/ 10 августа 2011

Чтобы определить, достаточно ли близко к нулю, чтобы оно печаталось как 0.000000 с шестью десятичными разрядами, что-то вроде:

fabs(d) < 0.0000005

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

Если вы хотите лучше понять, какое значение у вас есть, попробуйте печатать с %g вместо %f.

4 голосов
/ 10 августа 2011

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

Редактировать: tl; dr: возможно, вы делаете это неправильно.

4 голосов
/ 10 августа 2011

Вы можете сделать диапазон.Как -0,00001 <= х <= 0,00001 </p>

1 голос
/ 26 ноября 2013

Кроме того, одним из часто пропускаемых признаков числа с плавающей запятой являются денормализованные числа. Это числа, которые имеют минимальный показатель степени, но не вписываются в диапазон 0,5-1.

Эти числа ниже , чем FLT_MIN для числа с плавающей запятой, и DBL_MIN для значения типа double.

Распространенной ошибкой при использовании порога является сравнение двух значений или использование FLT_MIN / DBL_MIN в качестве предела.

Например, это может привести к нелогичному результату (если вы не знаете о ненормативной лексике):

bool areDifferent(float a, float b) {
    if (a == b) return false;  // Or also: if ((a - b) == FLT_MIN) 
    return true;
}


// What is the output of areDifferent(val, val + FLT_MIN * 0.5f) ?
// true, not false, even if adding half the "minimum value".

Денормали также обычно подразумевают потерю производительности в вычислениях. Тем не менее, вы не можете отключить их, иначе такой код все еще может вызвать исключение с плавающей точкой DIVIDE BY ZERO (если включено):

float getInverse(float a, float b) {
    if (a != b)
        return 1.0f / (a-b); // With denormals disabled, a != b can be true, but (a - b) can still be denormals, it'll rounded to 0 and throw the exception
    return FLT_MAX;
}
...