首页 > 解决方案 > NAN 差异 - std::nan vs quiet_NaN() vs Macro NAN

问题描述

我想知道以下类型之间的区别是什么nan。除了(与 just 相比,NAN_macro其评估结果为)的视觉差异外,它们的行为似乎都相同(根据下面的示例脚本)。-nan(ind)nan

我看了一些其他的答案,例如安静的 NaN 和信令 NaN 有什么区别?. 但我仍然不太明白为什么NANis -nan(ind)while std::numeric_limits<double>::quiet_NaN()is nan,或者为什么我们有std::nan("")std::nan("1")如果在一天结束时它们似乎都是同一个概念。

任何解释或澄清链接都会很棒。

#include <cmath>
#include <limits>

int main()
{
    using num_lim = std::numeric_limits<double>;

    const double NAN_macro   = static_cast<double>(NAN);  // -nan(ind)
    const double NAN_quiet   = num_lim::quiet_NaN();      // nan
    const double NAN_sig     = num_lim::signaling_NaN();  // nan
    const double NAN_str     = std::nan("");              // nan
    const double NAN_str1    = std::nan("1");             // nan
    const double NAN_strLong = std::nan("some string");   // nan

    const bool isnan_macro   = std::isnan(NAN_macro);    // true
    const bool isnan_quiet   = std::isnan(NAN_quiet);    // true
    const bool isnan_sig     = std::isnan(NAN_sig);      // true
    const bool isnan_str     = std::isnan(NAN_str);      // true
    const bool isnan_str1    = std::isnan(NAN_str1);     // true
    const bool isnan_strLong = std::isnan(NAN_strLong);  // true

    const bool not_equal_macro   = (NAN_macro   != NAN_macro);   // true
    const bool not_equal_quiet   = (NAN_quiet   != NAN_quiet);   // true
    const bool not_equal_sig     = (NAN_sig     != NAN_sig);     // true
    const bool not_equal_str     = (NAN_str     != NAN_str);     // true
    const bool not_equal_str1    = (NAN_str1    != NAN_str1);    // true
    const bool not_equal_strLong = (NAN_strLong != NAN_strLong); // true

    const double sum_macro   = 123.456 + NAN_macro;    // -nan(ind)
    const double sum_quiet   = 123.456 + NAN_quiet;    // nan
    const double sum_sig     = 123.456 + NAN_sig;      // nan
    const double sum_str     = 123.456 + NAN_str;      // nan
    const double sum_str1    = 123.456 + NAN_str1;     // nan
    const double sum_strLong = 123.456 + NAN_strLong;  // nan
}

标签: c++c++11nan

解决方案


IEEE 标准 754 表示具有NaN全 1 的指数和非零小数的位模式(请注意,所有浮点十进制值都由“符号”、“指数”和“小数”表示),然后,您可以表示很多不同的 NaN,因为“非零分数”可能有很多不同的值。

要突出显示您的NaN表示,请使用以下代码扩展您的代码:

#include <cmath>
#include <limits>
#include <bitset>
#include <iostream>

union udouble {
  double d;
  unsigned long long u;
};

void Display(double doubleValue, char* what)
{
    udouble ud;
    ud.d = doubleValue;
    std::bitset<sizeof(double) * 8> b(ud.u);
    std::cout << "BitSet : " << b.to_string() << " for " << what << std::endl;
}

int main()
{
    using num_lim = std::numeric_limits<double>;

    const double NAN_macro   = static_cast<double>(NAN);  // -nan(ind)
    const double NAN_quiet   = num_lim::quiet_NaN();      // nan
    const double NAN_sig     = num_lim::signaling_NaN();  // nan
    const double NAN_str     = std::nan("");              // nan
    const double NAN_str1    = std::nan("1");             // nan
    const double NAN_strLong = std::nan("some string");   // nan

    ...

    Display( NAN_macro, "NAN_macro" );
    Display( NAN_quiet, "NAN_quiet" );
    Display( NAN_sig, "NAN_sig" );
    Display( NAN_str, "NAN_str" );
    Display( NAN_str1, "NAN_str1" );
    Display( NAN_strLong, "NAN_strLong" );
}

即使它有一些 UB(可以修复,请参阅 Ruslan 评论),它确实可以说明我在下面解释的内容),该程序输出:

BitSet : 0111111111111000000000000000000000000000000000000000000000000000 for NAN_macro
BitSet : 0111111111111000000000000000000000000000000000000000000000000000 for NAN_quiet
BitSet : 0111111111110100000000000000000000000000000000000000000000000000 for NAN_sig
BitSet : 0111111111111000000000000000000000000000000000000000000000000000 for NAN_str
BitSet : 0111111111111000000000000000000000000000000000000000000000000001 for NAN_str1
BitSet : 0111111111111000000000000000000000000000000000000000000000000000 for NAN_strLong

他们都有:

  • “0”作为符号(您可能会为 NAN_macro 获得“1”,因为您将其报告为 -nan,但我没有)。我使用了 g++,我敢打赌NAN,某些编译器可能会以不同的方式定义宏,我怀疑这是 C++ 标准的一部分。
  • “11111111111”作为指数
  • 然后是“分数”的不同值……实际上是任何值,但绝不是零,否则它将不再是 NaN。

实际上,这个“分数”或“有效负载”(参见 Ruslan 注释)值可用于存储您想要存储的任何信息(如“为什么这里有一个 nan”?),这被称为NaN-boxing

这主要是为什么您在某些时候可能具有“不同”的 NaN 值(quiet_NaNsignaling_NaNNaN您创建的任何符合 IEEE 标准 754 的...即使具有不同的内存表示,它们都是NaN值(x!=xstd::isnan(x)==true)。

所以,回答你的问题:

为什么 NAN 是-nan(ind),而std::numeric_limits<double>::quiet_NaN()nan 是

可能是因为您的编译器这样定义了 NAN 宏,所以使用另一个编译器可能会有所不同。顺便说一句,它就像最小/最大宏,即使一个人有定义它们的坏主意,不要使用它们,更喜欢std作为标准一部分的函数,因此应该与您使用的任何编译器相同。

why we have std::nan("") and std::nan("1") if at the end of the day they both seem to be the same concept.

Maybe "to help you play with NaN-boxing" could be an answer, even if I doubt those functions were created for that specific purpose. The right answer may just be "to let you decide what "fraction" value you want to use for your NaN if you need something different than std::quiet_NaN and std::signaling_NaN"

Sources: https://steve.hollasch.net/cgindex/coding/ieeefloat.html

Also used https://stackoverflow.com/a/40737266/3336423 to output NaN memory representation.


推荐阅读