Делает ли «Эпсилон» действительно что угодно в вычислениях с плавающей точкой?

StackOverflow https://stackoverflow.com/questions/2729637

Вопрос

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

Чтобы убедиться, что результат имеет смысл, я могу проверить, 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;
}

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

Это было полезно?

Решение

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

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

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

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

Другие советы

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top