Как проверить и обработать числа, очень близкие к нулю - PullRequest
10 голосов
/ 08 августа 2011

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

В настоящее время я пробую следующее, это правильно?

if ( std::abs(x) < DBL_MIN ) {
     log_debug("detected small num, %Le, %Le", x, y);
}

Во-вторых, математика по своей природе является тригонометрической (то есть с использованием большого количества преобразований радиан / градусов и вызовов sin / cos / tan и т. Д.), Какие преобразования можно сделать, чтобы избежать математические ошибки?

Очевидно, что для умножения я могу использовать log log - что еще?

Ответы [ 4 ]

5 голосов
/ 18 февраля 2012

Вопреки распространенному мнению, DBL_MIN - это не наименьшее положительное значение double, а наименьшее положительное нормализованное значение double.Обычно - для 64-битного ieee754 doubles - это 2 -1022 , тогда как наименьшее положительное значение double составляет 2 -1074 .Поэтому

Я сейчас пробую следующее, это правильно?

if ( std::abs(x) < DBL_MIN ) {
     log_debug("detected small num, %Le, %Le", x, y);
}

может иметь утвердительный ответ.Условие проверяет, является ли x денормализованным (также называемым ненормальным ) числом или ± 0,0.Не зная больше о вашей конкретной ситуации, я не могу сказать, подходит ли этот тест.Денормализованные числа могут быть законными результатами вычислений или следствием округления, где правильный результат будет равен 0. Также возможно, что округление дает числа гораздо большей величины, чем DBL_MIN, когда математически правильный результат будет равен 0, поэтому намного большепорог может быть разумным.

2 голосов
/ 08 августа 2011

Поскольку вы используете C ++, наиболее идиоматичным является использование std::numeric_limits из заголовка <limits>.

Например:

template <typename T>
bool is_close_to_zero(T x)
{
    return std::abs(x) < std::numeric_limits<T>::epsilon();
}

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

Также могут быть полезны std::numeric_limits<T>::min() и std::numeric_limits<T>::denorm_min().Первое - это наименьшее положительное неденормированное значение типа T (равно FLT/DBL/LDBL_MIN из <cfloat>), второе - наименьшее положительное значение типа T (без <cfloat> эквивалента).

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

2 голосов
/ 08 августа 2011

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

Вы также можете попробовать перехватить сигнал SIGFPE, который сработает на POSIX-совместимомСистема в любое время, когда есть математическая ошибка, включая смещение с плавающей точкой.См .: http://en.wikipedia.org/wiki/SIGFPE

РЕДАКТИРОВАТЬ : Для ясности, DBL_MIN НЕ является наибольшим отрицательным значением, которое может содержать двойник, это наименьшее положительное значение нормализовано значение, которое может содержать двойник.Таким образом, ваш подход хорош, пока значение не может быть равно нулю.

Еще одна полезная константа - DBL_EPSILON, которая является наименьшим двойным значением, которое можно добавить к 1,0, не возвращая 1.0.Обратите внимание, что это намного большее значение, чем DBL_MIN.Но это может быть полезно для вас, поскольку вы выполняете тригонометрические функции, которые могут стремиться к 1, а не к 0.

0 голосов
/ 08 августа 2011

Первая проверка if будет на самом деле верна только тогда, когда ваше значение равно нулю.

Для второго вопроса вы подразумеваете много конверсий.Вместо этого выберите одну единицу измерения (градусы или градусы) и выполните все свои вычислительные операции в этой единице.Затем в самом конце сделайте однократное преобразование в другое значение, если вам нужно.

...