Как вы проверяете бесконечные и неопределенные значения в C++?


В моих программах бесконечность обычно возникает, когда значение делится на ноль. Я становлюсь неопределенным, когда делю ноль на ноль. Как вы проверяете бесконечные и неопределенные значения в C++?

В C++ бесконечность представлена 1.#БЕСКОНЕЧНОСТЬ. Неопределенное представлено значением -1.#IND. Проблема заключается в том, как проверить, является ли переменная бесконечной или неопределенной. Проверка бесконечности относительно проста: вы находите определение бесконечности в вашем конкретном C++. Для моего случая (VS2003), это std:: numeric_limits:: infinity(). Вы должны включить "ограничения", чтобы использовать его. Вы можете присвоить это бесконечное значение переменной и сравнить его с некоторым значением, чтобы проверить, является ли это значение бесконечным.

Неопределенное - это немного сложно, потому что вы не можете сравнить неопределенное значение с каким-то другим значением. Любое сравнение возвращает false. Это свойство можно использовать для определения неопределенного значения, сравнивая его с самим собой. Допустим, у вас есть двойная переменная под названием aVal. В нормальных условиях-Аваль != aVal возвращает false. Но если значение неопределенно, aIndVal != aIndVal возвращает true. Эта странная ситуация не существует для бесконечных значений, то есть aInfVal != aInfVal всегда возвращает false. Вот две функции, которые можно использовать для проверки неопределенных и бесконечных значений:
#include "limits.h"
#include "math.h"

bool isIndeterminate(const double pV)
{
    return (pV != pV);
} 

bool isInfinite(const double pV)
{
    return (fabs(pV) == std::numeric_limits::infinity())
}

Есть ли лучшие способы для этих проверок, я что-нибудь упускаю?

6 28

6 ответов:

Для Visual Studio я бы использовал _isnan и еще _finite, или, возможно,_fpclass.

Но если у вас есть доступ к стандартной библиотеке и компилятору C++11, вы можете использовать std::isnan и еще std::isinf.

Хотя C++03 не предоставляет C99 isnan и isinf макросы , C++11 стандартизирует их, предоставляя их в виде функций . Если вы можете использовать C++11 вместо строгого C++03, то это будут более чистые варианты, избегая макросов, встроенных компиляторов и зависящих от платформы функций.

C++11's std::isfinite возвращает true для всех значений, кроме inf и nan; поэтому !isfinite должен проверять бесконечные и неопределенные значения в одном выстрел.

Хотя и не является строго частью C++03, Если ваш компилятор предоставляет некоторые из новых функций C99 стандарта заголовочный файл, то у вас может быть доступ к следующим "функциональным макросам": isfinite, isinf, isnan. Если это так, то это будет самый простой и безопасный способ выполнить эти проверки.

Вы также можете использовать их как строгое решение только для C++. На самом деле они не предлагают больше, чем решение OP, за исключением дополнительной безопасности за счет использования черт типа и, возможно, самого крошечного повышения скорости в случае is_inf.

template <bool> struct static_assert;
template <> struct static_assert<true> { };

template<typename T>
inline bool is_NaN(T const& x) {
    static_cast<void>(sizeof(static_assert<std::numeric_limits<T>::has_quiet_NaN>));
    return std::numeric_limits<T>::has_quiet_NaN and (x != x);
}

template <typename T>
inline bool is_inf(T const& x) {
    static_cast<void>(sizeof(static_assert<std::numeric_limits<T>::has_infinity>));
    return x == std::numeric_limits<T>::infinity() or x == -std::numeric_limits<T>::infinity();
}

(остерегайтесь самодельных static_assert)

Есть isfinite из C99 или POSIX или что-то еще, я думаю.

Один из хакерских способов сделать это-проверить x-x == 0; если x бесконечен или NaN, то x-x является NaN, поэтому сравнение не удается, а если x конечен, то x-x является 0 и сравнение успешно. Однако я бы рекомендовал использовать isfinite или упаковывать этот тест в функцию / макрос, называемую чем-то вроде isfinite, чтобы вы могли избавиться от всего этого, когда придет время.
if (x!=x)              ... then x is nan
if (x>0 && x/x != x/x) ... then x is +inf
if (x<0 && x/x != x/x) ... then x is -inf

Это также может сработать (но включает вызов exp() и проверку равенства двойников):

if (exp(-x)==0.) ... then x is inf
if (exp(x)==0.)  ... then x is -inf