什么规则控制将 static_cast<float> 应用于双精度的舍入行为?

Gav*_*ley 5 c++ floating-point casting

如果我们在 C++ 中有一个双精度值并对其进行 astatic_cast<float>处理,返回值的绝对值是否总是较小?我的直觉在这背后说是的,原因如下。

  • 可能的单精度指数集严格是双精度指数的子集
  • 在将双精度尾数转换为单精度时,位可能会被截断然后结束以使双精度尾数适合浮点数尾数。但是,如果四舍五入更准确,有时会将其向上舍入到下一个最高浮点值并非不可能。也许这取决于系统,或者在某些标准中定义。

我在以下程序中对此进行了一些数值实验。似乎有时会发生向上取整,而其他时候则向下取整。

在哪里可以找到有关如何期望此舍入行为的更多信息?它是否总是四舍五入到最近的浮点数?

#include <cmath>
#include <iostream>

int main() {
  // Start testing double precision values starting at x, going up to max
  double x = 0.98;
  constexpr double max = 1e10;

  // Loop over many possible double-precision values, print out
  // if casting to float ever produced a larger number.
  int output_counter = 0; // output every n steps
  constexpr int output_interval = 100000000;

  std::cout.precision(17);
  while (x < max) {
    // volatile to ensure compiler doesn't optimize this out
    volatile float xprime = static_cast<float>(x);
    double xprimeprime = static_cast<double>(xprime);

    if (xprimeprime > x)
      std::cout << "Found a round up! x=" << x << ", xprime = "<< xprime << std::endl;

    // Go to the next higher double precision value
    x = std::nextafter(x, std::numeric_limits<double>::infinity());

    output_counter++;
    if (output_counter == output_interval) {
      std::cout << x << std::endl;
      output_counter = 0;
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

Sha*_*dow 5

标准在[conv.double] 中说:

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

请注意,使用<limits>标题,您可以通过std::numeric_limits<T>::round_style. 有关可能的值,请参阅[round.style]。(至少我假设浮点转换属于浮点运算。)


Adr*_*ica 5

我在通常用于回答此类问题的C++17 标准草案中找不到明确的答案1 ; 然而,cppreference(通常是可靠的)强烈建议浮点转换的舍入模式是实现定义的

\n

然而,它还指出,如果遵循 IEEE-754 规则,则舍入到最接近的可表示值2

\n
\n

浮点转换
\n浮点类型的纯右值可以转换为任何其他浮点类型的纯右值。\n如果转换列在浮点提升下,则它是提升而不是转换。

\n
    \n
  • 如果源值可以在目标类型中精确表示,则它不会改变。如果源值位于目标类型的两个可表示值之间,则结果是这两个值之一(哪个值是实现定义的,尽管如果支持 IEEE 算术,则默认舍入到最接近的值)。
  • \n
  • 否则,行为是未定义的。
  • \n
\n
\n

此外,可以使用函数以及标头中定义的以下舍入模式之一来更改引用的IEE-754默认行为:std::fesetround(int round)<cfenv>

\n
#define FE_DOWNWARD     /*implementation defined*/ // (since C++11)\n#define FE_TONEAREST    /*implementation defined*/ // (since C++11)\n#define FE_TOWARDZERO   /*implementation defined*/ // (since C++11)\n#define FE_UPWARD       /*implementation defined*/ // (since C++11)\n
Run Code Online (Sandbox Code Playgroud)\n
\n

1 BlameTheBits 在标准中找到了相关部分。在我提到的 C++17 草案中,这实际上是\xc2\xa77.9.1,但其他方面类似。

\n

2 IEEE-754 实际上定义了5 种不同的浮点舍入规则。

\n