Как лучше всего использовать 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, которая будет вызывать исключение с плавающей запятой при использовании?
@JeffreyMartinez: Это не обычное исключение C++, если вы так думаете. Это исключение с плавающей запятой: см. Примечания здесь.





Вы можете записать сигнальный NaN в переменную, не вызывая исключения, примерно так (nb: непроверено)
void set_snan( double &d )
{
long long *bits = (long long *)&d;
*bits = 0x7ff0000080000001LL;
}
Он будет работать в большинстве мест, но нет, он не на 100% портативен.
В вашей реализации C++ может быть API для доступа к среде с плавающей запятой для проверки и удаления определенных исключений с плавающей запятой. См. мой ответ на связанный вопрос для получения дополнительной информации.
Что ж, глядя на определение как тихого, так и сигнального NaN, я не вижу никакой разницы.
Вы можете сами использовать код, который используется в этих функциях, возможно, он таким образом предотвращает исключение, но, не видя исключения в этих двух функциях, я думаю, что это может быть связано с чем-то другим.
Если вы хотите напрямую назначить NaN:
double value = _Nan._Double;
Что означает сигнализация 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;
}
};
boost::optional<T> - хорошая альтернатива этому и хорошо работает для возвращаемых значений, локальных переменных и членов. Кроме того, он не вызывает конструктор по умолчанию и работает по большей части со ссылочными типами.
Посмотрев на это еще немного, похоже, что signaling_NaN бесполезен, как и предусмотрено. Если включены исключения с плавающей запятой, то его вызов считается обработкой сигнального NaN, поэтому немедленно возникает исключение. Если исключения с плавающей запятой отключены, то обработка сигнального NaN автоматически понижает его статус до тихого NaN, поэтому signaling_NaN не работает в любом случае.
Кодекс Менкбоя работает, но попытка использовать сигнальные NaN сталкивается с другими проблемами: нет переносимого способа включения или отключения исключений с плавающей запятой (как указано в здесь и здесь), и если вы полагаетесь на включение исключений, сторонний код может отключить их (как описано здесь).
Так что кажется, что Решение Мотти действительно лучший выбор.
Простой ответ: Сделайте что-нибудь подобное в заголовочном файле и используйте его везде:
#define NegativeNaN log(-1)
Если вы хотите проделать с ними какие-то манипуляции, лучше напишите какую-нибудь расширенную функцию-оболочку вокруг exp(), например extended_exp() и так далее!
хм ... Я играю с этим, потому что мне сейчас любопытно, но я не мог заставить свой создать исключение. Что вы сделали, чтобы получить исключение?