33

在 C++ 中使用 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使用时会引发浮点异常?

4

6 回答 6

11

在进一步研究之后,它看起来像signaling_NaN提供的那样没用。如果启用了浮点异常,则调用它算作处理信号 NaN,因此它会立即引发异常。如果浮点异常被禁用,则处理信号 NaN 会自动将其降级为安静的 NaN,因此signaling_NaN这两种方式都不起作用。

Menkboy 的代码有效,但尝试使用信号 NaN 会遇到其他问题:没有可移植的方法来启用或禁用浮点异常(如这里这里所提到的),如果您依赖于启用的异常,第三方代码可能禁用它们(如此所述)。

所以看起来莫蒂的解决方案确实是最好的选择。

于 2008-11-11T18:32:35.827 回答
9

信号 NAN 的意思是当 CPU 遇到它时会触发一个信号(因此得名)。如果您想检测未初始化的变量,那么提高编译器的警告级别通常会检测到所有使用未初始化值的路径。如果您无法使用存储布尔值的包装类来说明值是否已初始化:

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; 
   }
};
于 2008-10-25T20:02:27.903 回答
3

您可以将信号 NaN 写入变量而不会触发类似这样的异常(nb:未经测试)

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

它适用于大多数地方,但不,它不是 100% 便携的。

于 2008-10-24T22:35:59.523 回答
3

好吧,看看安静和信号 NaN 的定义,我真的无法区分。

您可以自己使用这些函数中使用的代码,也许它可以防止异常,但是在这两个函数中没有看到异常,我认为它可能与其他东西有关。

如果要直接分配 NaN:

double value = _Nan._Double;
于 2008-10-25T19:57:05.107 回答
3

简单的答案:在头文件中做这样的事情并在其他地方使用它:

#define NegativeNaN log(-1)

如果您希望对它们进行某种操作,最好在exp()likeextended_exp()等周围编写一些扩展的包装函数!

于 2012-11-05T15:49:34.910 回答
0

您的 C++ 实现可能具有用于访问浮点环境以测试和清除某些浮点异常的 API。有关更多信息,请参阅我对相关问题的回答

于 2008-10-24T22:42:48.773 回答