浮点 - 在C++中使用 NaN

floating point - Using NaN in C++?

本文关键字:NaN C++ 浮点      更新时间:2023-10-16

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

在进一步研究之后,看起来signaling_NaN是无用的。 如果启用了浮点异常,则调用它计为处理信令 NaN,因此它会立即引发异常。 如果禁用浮点异常,则处理信令 NaN 会自动将其降级为安静 NaN,因此signaling_NaN无论哪种方式都不起作用。

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

因此,看起来Motti的解决方案确实是最佳选择。

信令 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; 
   }
};

您可以将信令 NaN 写入变量,而不会触发异常(注意:未经测试)

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

它可以在大多数地方工作,但不,它不是 100% 便携的。

好吧,考虑到安静和信号 NaN 的定义,我真的无法区分任何区别。

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

如果要直接分配 NaN:

double value = _Nan._Double;

简单的答案:在头文件中执行类似操作,并在其他任何地方使用它:

#define NegativeNaN log(-1)

如果你想对它们进行某种操作,最好围绕exp()编写一些扩展的包装器函数,比如extended_exp()等等!

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