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