Использование NaN в C ++? - PullRequest
       75

Использование NaN в C ++?

32 голосов
/ 25 октября 2008

Как лучше всего использовать NaN в C ++?

Я нашел std::numeric_limits<double>::quiet_NaN() и std::numeric_limits<double>::signaling_NaN(). Я хотел бы использовать signaling_NaN для представления неинициализированной переменной следующим образом:

double diameter = std::numeric_limits<double>::signaling_NaN();

Это, однако, сигнализирует (вызывает исключение) при назначении. Я хочу, чтобы это выдало исключение при использовании, а не при назначении.

Есть ли способ использовать signaling_NaN, не вызывая исключения при назначении? Есть ли хорошая, переносимая альтернатива signaling_NaN, которая вызовет исключение с плавающей запятой при использовании?

Ответы [ 6 ]

11 голосов
/ 11 ноября 2008

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

Код Менкбоя работает, но попытка использовать сигнальные NaN наталкивается на другие проблемы: не существует переносимого способа включения или отключения исключений с плавающей запятой (как здесь упоминается и здесь ), и если вы полагаетесь на то, что исключения включены, сторонний код может их отключить (как описано здесь ).

Так что, похоже, Решение Мотти действительно лучший выбор.

9 голосов
/ 26 октября 2008

Что означает сигнализация NAN, так это то, что когда процессор встречает его, запускается сигнал (отсюда и название). Если вы хотите обнаружить неинициализированные переменные, то повышение уровня предупреждения в вашем компиляторе обычно обнаруживает все пути, которые используют неинициализированные значения. В противном случае вы можете использовать класс-оболочку, который хранит логическое выражение, если значение инициализировано:

template <class T>
class initialized {
    T t;
    bool is_initialized;
public:
    initialized() : t(T()), is_initialized(false) { }
    initialized(const T& tt) : t(tt), is_initialized(true) { }
    T& operator=(const T& tt) { t = tt; is_initialized = true; return t; }
    operator T&() {
         if (!is_initialized)
             throw std::exception("uninitialized");
         return t; 
   }
};
3 голосов
/ 05 ноября 2012

Простой ответ: Сделайте что-то подобное в заголовочном файле и используйте его везде:

#define NegativeNaN log(-1)

Если вы хотите выполнить какие-то манипуляции с ними, лучше напишите некоторую расширенную функцию-обертку вокруг exp(), например extended_exp() и так далее!

3 голосов
/ 25 октября 2008

Ну, глядя на определение как тихого, так и сигнального NaN, я не могу разглядеть никакой разницы.

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

Если вы хотите напрямую назначить NaN:

double value = _Nan._Double;
3 голосов
/ 25 октября 2008

Вы можете записать сигнальный NaN в переменную, не вызывая исключения с чем-то вроде этого (nb: untested)

void set_snan( double &d )
{
    long long *bits = (long long *)&d;
    *bits = 0x7ff0000080000001LL;
}

Это будет работать в большинстве мест, но нет, оно не на 100% портативно.

0 голосов
/ 25 октября 2008

Ваша реализация C ++ может иметь API для доступа к среде с плавающей запятой для проверки и очистки определенных исключений с плавающей запятой. См. мой ответ на связанный вопрос для получения дополнительной информации.

...