Подтвердить что ты не робот

Сравнение чисел с плавающей запятой в C

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

4b9b3361

Ответ 1

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

fabs(d) < 0.0000005

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

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

Ответ 2

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

Ответ 3

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

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

Ответ 4

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

Эти числа ниже, чем FLT_MIN для float, и 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;
}