【问题标题】:When does NaN not propagate in C++?NaN 何时不在 C++ 中传播?
【发布时间】:2018-08-10 13:27:59
【问题描述】:

NaN 通过 NaN - Wikipedia 中描述的“大多数”操作传播。

我想知道 NaN 不传播的操作。例如,我在 C++ 中编码,发现下面的代码打印出1,这不是 NaN。

const double result = std::pow(1, std::numeric_limits<double>::quiet_NaN());
std::cout << result << std::endl;

对于std::powfunction,此行为在std::pow - cppreference.com 中进行了描述。

你能分享其他的例子吗?

【问题讨论】:

  • 请注意,维基百科的描述强烈遵循 IEEE754,但 C++ 不强制要求 IEEE754。甚至对浮点类型使用 IEEE754 format 的实现也可能不使用 IEEE754 rules

标签: c++ nan


【解决方案1】:

std::pow 并不是真正的运算符,因为a * b 中的乘法之类的东西是一个运算。这是一个函数。函数可以有分支,可以随意处理NaN。另一个类似的std 函数在某种意义上是std::is_nan。这也不会传播NaN:它返回一个bool,它可以隐式转换为数字类型:

std::is_nan(std::is_nan(a))

对于允许表达式编译的任何类型afalse。其他示例包括std::fpclassifystd::isfinite。我特别喜欢以std::fpclassify 为例,因为它有一个int 返回类型,所以实际上它不亚于pow 的数字函数!

请注意,根据this question 的公认答案,NaNbool 的隐式转换被定义为 false true,这很重要。它允许!&amp;&amp;||NaN 一起工作。最后,表达式分隔符操作符, 也不会传播NaN,并且三元条件操作的未使用分支上的NaN 将被忽略。

【讨论】:

  • 也转换为布尔值:NaN -> false。
  • "它不亚于pow" 呵呵,不错的尝试;)
  • “C++ 中没有不传播 NaN 的内置运算符,三元条件运算符除外。”好吧,!&amp;&amp;||。和,。更不用说关系运算符了。
  • @Cheersandhth.-Alf:确实通过隐式转换为bool。我已经添加了。可能是时候对这个答案进行维基百科了。你怎么看?
  • std::is_nan 是一个测试/分类函数,与 pow 不同,我永远不会期望它返回 NaN。
【解决方案2】:

我会尝试的另一种情况是pow(0, NaN)。如果有pow(0,0) == 1,那么应该期待

 pow(0, NaN) == 1

这是有原因的,实际上它是一致行为所必需的。 尽管没有 IEEE 标准规定所有基本函数关于 NaN 的行为,但有一个非常基本的规则:

如果对于所有有限点数x,包括 +inf 和 -inf(但不包括 NaN)我们有

f(const1, x) == const2

那么(并且只有这样)一个也必须返回一个非 NaN 结果

f(const1, NaN) == const2

#长解释

这是因为 NaN 表示“未定义”或“来自 -inf .. inf 的任何其他数值”。为什么?考虑原型示例 0/0。 如果有方程

 b = a * x

并且想要解决x。显然解决方案是

x = b/a

现在如果a == b == 0 则原方程有无穷多个解,即所有有限数x。这就是为什么 NaN 表示“未指定”和0/0 == NaN 的原因。现在,如果一个未指定的数字(即 NaN)作为参数输入函数,它通常会导致“未指定”的答案。例外情况是输出不依赖于输入,在这种情况下应该/不得返回 NaN。

考虑

pow(1, a/b)

对于非零 ab,此表达式的计算结果始终为 1,这从数学角度来看是有意义的,因为数学意义上的 1^x 不依赖于 x。因此,在数字上,对于a = 0b = 0(以及因此x=NaN),人们也将要求获得1。

如果有人同意这一事实

pow(1,-inf) = pow(1,inf) = 1 

那么你也必须定义

pow(1,NaN) = 1

为了一致的行为。这同样适用于pow(0,c),但对零次多项式的评估也应在 NaN 输入上生成非 NaN 输出。

注意:此推理可应用于任何函数,包括内置运算符。

【讨论】:

  • @cmaster:是的,是的。我以为我更改了所有 exp -> pow,但一开始就错过了。谢谢。
【解决方案3】:

这是一个示例,演示返回非 NaN 的 NaN 函数。该列表在 IEEE 754-2008 中,9.2.1 特殊值(还有一些其他功能,但它们似乎没有在 C++ 中实现):

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

int main()
{
    const auto nan=std::numeric_limits<double>::quiet_NaN();
    const auto inf=std::numeric_limits<double>::infinity();
    std::cout << std::hypot(nan,inf) << '\n';
    std::cout << std::hypot(inf,nan) << '\n';
    std::cout << std::pow(nan, 0) << '\n';
    std::cout << std::pow(1,nan) << '\n';
}

输出是:

inf
inf
1
1

【讨论】:

    猜你喜欢
    • 2013-03-11
    • 2018-08-07
    • 2019-04-06
    • 2011-04-06
    • 1970-01-01
    • 1970-01-01
    • 2010-09-19
    • 2019-09-04
    • 2011-03-27
    相关资源
    最近更新 更多