“Эпсилон” действительно гарантирует что-нибудь в вычислениях с плавающей точкой?

Для создания проблемы короткой скажем, я хочу вычислить выражение a / (b - c) на floats.

Для проверки результат значим, я могу проверить если b и c находятся в равном:

float EPS = std::numeric_limits<float>::epsilon();
if ((b - c) > EPS || (c - b) > EPS)
{
    return a / (b - c);
}

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

Случай 1:

a = 1.0f;
b = 0.00000003f;
c = 0.00000002f;

Результат: если бы условие НЕ соблюдают, но выражение привело бы к корректному результату 100000008 (что касается точности плаваний).

Случай 2:

a = 1e33f;
b = 0.000003;
c = 0.000002;

Результат: если условие соблюдают, но выражение приводит не к значимому результату +1.#INF00.

Я нашел это намного более надежным для проверки результата, не аргументов:

const float INF = numeric_limits<float>::infinity();
float x = a / (b - c);
if (-INF < x && x < INF)
{
     return x;
}

Но что для эпсилон затем и почему все говорят, что эпсилон хорош для использования?

17
задан phuclv 2 November 2016 в 11:32
поделиться

2 ответа

«При работе с числами с плавающей запятой вы должны использовать эпсилон» - это резкая реакция программистов с поверхностным пониманием вычислений с плавающей запятой для сравнений в вообще (не только до нуля).

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

Если вы не читали Что должен знать каждый компьютерный ученый об арифметике с плавающей запятой , это хорошая отправная точка. Кроме того, если вас интересует точность результата деления в вашем примере, вы должны оценить, насколько неточным bc были предыдущие ошибки округления, потому что действительно, если bc мала, небольшая абсолютная ошибка соответствует большой абсолютной ошибке результата.Если вас беспокоит только то, чтобы разделение не переполнялось, тогда ваш тест (по результату) верен. Нет причин проверять делитель нуля с числами с плавающей запятой, вы просто проверяете переполнение результата, которое фиксирует как случаи, когда делитель равен нулю, так и когда делитель настолько мал, что делает результат не представляемым с помощью любая точность.

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

19
ответ дан 30 November 2019 в 13:40
поделиться

Epsilon используется для определения того, достаточно ли близки два числа, подверженных ошибке округления, чтобы их можно было считать «равными». Обратите внимание, что лучше тестировать fabs (b / c - 1) , чем fabs (bc) , и даже лучше - благодаря конструкции IEEE float - test abs (* (int *) & b - * (int *) & c) (где EPSI - небольшое целое число).

Ваша проблема другого характера и, вероятно, требует проверки результата, а не исходных данных.

2
ответ дан 30 November 2019 в 13:40
поделиться
Другие вопросы по тегам:

Похожие вопросы: