【问题标题】:Can a floating-point conversion cause undefined behavior?浮点转换会导致未定义的行为吗?
【发布时间】:2022-11-16 11:08:22
【问题描述】:

正如标准所定义的那样,浮点转换是两种不是提升的浮点类型之间的转换。

最简单的例子是doublefloat

double d = 0.1;
float f = d;

标准说[conv.double]

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

在我上面的示例中,源值不能在目标类型中准确表示。 d 的值是0.10000000000000001,而f 的值(很可能)是0.10000000149011612,事实上,如果您将f 转换回double,则不等于d。但是,此源值介于两个相邻的目标值之间:f 和先前可表示的float0.099999994039535522。所以 f 的值可以是这些值中的任何一个,但是因为 0.100000001490116120.099999994039535522 更接近 0.10000000000000001 ,所以这可能是实现选择的值。

我的问题是关于最后一个案例:

否则,行为未定义。

是否存在转换为未定义行为的任​​何值?由于浮点类型具有 +infinity 和 -infinity 的表示形式,因此我假设不存在任何未完全表示或位于两个相邻目标值之间的源值:任何 double 值要么是精确的 float 值(包括 NaN) 或介于 -infinity 和 +infinity 之间,在这种情况下它位于两个相邻的 float 值之间。

那么这个“否则”的案例有什么意义呢?此处是否涵盖被视为浮点但不是 floatdoublelong double 的奇异类型? floatdoublelong double 之间的转换会导致未定义的行为吗?

【问题讨论】:

  • IANALL,“否则,行为未定义”。可执行定义(良好定义),作为一个扩大到标准。这可能会使您的程序在您的平台上具有可靠的、可预测的行为,但可能不像人们喜欢的那样可移植(这可能会在未来咬到你,就像我一样)。
  • @Eljay 问题不是“未定义行为是什么意思”,而是“你怎么可能得到那个说它是未定义行为的案例”。
  • C++ 标准不要求每个浮点类型都支持+inf-infNaN。我怀疑这个“未定义”子句会处理一个假设的平台,其中 double 持有无穷大被转换为不支持无穷大的 float
  • floatdouble 不必是 IEEE 754 浮点类型。不确定是否存在不同的编码,您会落入 UB 案例,但标准已根据在存在 ab 异常的情况下的行为进行了编纂。
  • @DrewDormann 好的,我通过查看std::numeric_limits<T>::has_infinity 意识到了这一点。任何不支持无穷大的浮点类型的例子?在这种情况下,即使只是一个大的 double(不是 +inf)转换为没有 +inf 的 float 也会结束。

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


【解决方案1】:

事实证明,某些浮点实现不能表示无穷大。正如 Eljay 指出的那样,MBF 就是其中之一。 HUGE_VAL 的存在也暗示了这一点,如果可能的话,它与 INFINITY 相同。

然而,这是极不可能的,可以使用 std::numeric_limits<T>::has_infinity 进行测试。据推测,如果此值为真,则浮点转换不会有任何未定义的行为。

【讨论】:

    【解决方案2】:

    浮点转换会导致未定义的行为吗?

    是的。


    考虑 2 个 float 值:FLT_MAXnextafterf(FLT_MAX, 0)。他们的区别是delta。全部保存在double中。

    random(a, b)形成一个double随机值(a, b]。

    double max = FLT_MAX;
    double max_before = nextafterf(FLT_MAX, 0);
    double delta = max - max_before;
    
    // Conversion to `float` is well defined
    double in_between = max_before + random(0.0, delta);
    float in_betweenf = in_between;  // in_between is inclusively between 2 float.
    
    // Conversion to `float` can fail as the `double` value can
    // exceed FLT_MAX, even is the sum is the smallest `double` more than `FLT_MAX`.
    double in_between = max + random(0.0, delta);
    float in_betweenf = in_between;
    

    这主要是在 float 不支持的情况下无穷.

    如果被转换的值超出了可以表示的值范围,则行为未定义。 C17dr § 6.3.1.5 1。


    理想情况下,如果 in_between = max_before + delta + random(0.0, 0.5*delta); 定义明确会很好,但当 float 缺少无穷大时就不行了。

    【讨论】:

      猜你喜欢
      • 2014-08-11
      • 2016-09-04
      • 1970-01-01
      • 2017-01-30
      • 1970-01-01
      • 2013-04-24
      • 1970-01-01
      • 1970-01-01
      • 2019-09-29
      相关资源
      最近更新 更多