【问题标题】:Is static_cast<double>(std::nanf("")) well defined?static_cast<double>(std::nanf("")) 定义明确吗?
【发布时间】:2018-10-31 03:26:44
【问题描述】:

标题几乎要求一切,但要提供一个 MCVE:

#include <cmath>

int main()
{
    float f = std::nanf("");
    double d = static_cast<double>(f);
    return 0;
}

在 MSVC 2017 下,fd 都报告为 nan,但这并不能证明什么,因为 static_cast 可能是未定义的行为。

以类似的方式,0.0f / 0.0f 产生-nan(ind),我将假设它是一个信号 nan,这是否遵循相同的定义/未定义规则?同上inf

【问题讨论】:

标签: c++ floating-point language-lawyer nan


【解决方案1】:

这看起来是由标准保证的,我们可以从 [expr.static.cast]p4 部分开始,它说:

如果存在从 e 到 T 的隐式转换序列,则表达式 e 可以显式转换为类型 T ...

隐式转换序列包含在[over.best.ics] 中,我们有以下内容:

格式良好的隐式转换序列是以下形式之一:
- (3.1) 一个标准的转换序列,
...

并且标准转换序列包含在[over.ics.scs]中,它说:

[ 注意:如 [conv] 中所述,标准转换序列要么是 Identity 转换本身(即不转换),要么由其他四个类别的一到三个转换组成。 如果序列中有两个或多个转换,则按规范顺序应用转换:左值转换、提升或转换、资格调整。 — 尾注 ]

我们在[conv.fpprom]p1 中介绍了浮点提升案例,其中说:

float 类型的纯右值可以转换为 double 类型的纯右值。 值不变。

以另一种方式 doublefloat 将是一种转换,这在 [conv#double]p1:

中有介绍

浮点类型的纯右值可以转换为另一种浮点类型的纯右值。 如果源值可以在目标类型中精确表示,则转换的结果就是该精确表示。 如果源值介于两个相邻的目标值之间,则转换的结果是实现定义的选择这些值中的任何一个。否则,行为未定义。

这种情况取决于源是否可以精确地由目标表示,但不能保证。

浮点除以零的子问题很复杂,在我对The behaviour of floating point division by zero 的回答中有所涉及。

【讨论】:

    【解决方案2】:

    static_cast 只是 promotingfloat,它总是被定义的。除以零是undefined(即使没有任何转换),尽管根据 IEEE 754 规则,许多实现允许它用于浮点类型。在普通系统上从未遇到过信令 NaN;改为使用floating-point exceptions

    【讨论】:

    • 如果不正常遇到信令 nans,从 std::nanf("") 获得的 nan 和除以零的 -nan(ind) 有什么区别?请注意,这些是我检查变量时 IDE 显示的内容。我知道标准未定义除以零,但对于我正在使用的当前 MSVC 编译器,这恰好是结果。显然,我永远不会在生产代码中依赖它,但这现在是学术好奇心的问题。
    • @dgnuff:有效位可以区分 NaN 并携带附加信息。对于产生 NaN 的操作,英特尔硬件会产生一个 NaN,英特尔硬件将其称为不确定的 NaN。那就是向您显示的“-nan(ind)”。由std::nanf("") 制作的那个只是有不同的位,是由软件制作的。 (软件也可以生成“-nan(ind)”;没有什么特别之处。)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-17
    • 2014-12-12
    • 1970-01-01
    • 2018-08-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多