小于 1 的浮点类型可表示的最大值

0xb*_*00d 24 c++ floating-point epsilon c++17

float有没有办法获得小于 的浮点类型可表示的最大值1。

我看过以下定义:

static const double DoubleOneMinusEpsilon = 0x1.fffffffffffffp-1;
static const float FloatOneMinusEpsilon = 0x1.fffffep-1;
Run Code Online (Sandbox Code Playgroud)

但这真的是我们应该如何定义这些价值观吗?

根据标准,std::numeric_limits<T>::epsilon是机器 epsilon,即 1.0 与浮点类型可表示的下一个值之间的差T。但这并不一定意味着定义T(1) - std::numeric_limits<T>::epsilon会更好。

Adr*_*ica 26

您可以使用std::nextafter函数,尽管它的名称如此,但它可以通过使用适当的参数来检索在算术上在给定起点之前的下一个可表示值to。(通常-Infinity、0、 或+Infinity)。

\n

根据 的定义nextafter,无论您的 C++ 实现使用什么浮点格式,这都是可移植的。(二进制与十进制,或尾数的宽度,又称有效数,或其他任何东西。)

\n

示例:检索类型的小于 1 的最接近值double(在 Windows 上,使用 Visual Studio 2019 中的 clang-cl 编译器),答案与计算结果不同1 - \xce\xb5(如注释中所述,对于 IEEE754 数字来说是不正确的) ;在 2 的任意幂以下,可表示的数字的接近程度是其以上的两倍):

\n
#include <iostream>\n#include <iomanip>\n#include <cmath>\n#include <limits>\n\nint main()\n{\n    double naft = std::nextafter(1.0, 0.0);\n    std::cout << std::fixed << std::setprecision(20);\n    std::cout << naft << \'\\n\';\n    double neps = 1.0 - std::numeric_limits<double>::epsilon();\n    std::cout << neps << \'\\n\';\n    return 0;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

输出:

\n
0.99999999999999988898\n0.99999999999999977796\n
Run Code Online (Sandbox Code Playgroud)\n

使用不同的输出格式,这可以打印为0x1.fffffffffffffp-1and 0x1.ffffffffffffep-1( 1 - \xce\xb5)

\n
\n

请注意,当使用类似技术确定大于 的最接近值时, 1调用nextafter(1.0, 10000.)将给出与计算相同的值1 + \xce\xb5(1.00000000000000022204),正如 \xce\xb5 的定义所预期的那样。

\n
\n

表现

\n

C++23 要求std::nextafter是constexpr,但目前只有一些编译器支持。GCC 确实通过它进行恒定传播,但 clang 不能(Godbolt)。0x1.fffffffffffffp-1;如果您希望它与 IEEE754 二进制 64系统中的文字常量一样快(启用优化)double,则在某些编译器上您将不得不等待 C++23 支持的该部分。(很可能一旦编译器能够做到这一点,比如 GCC,即使没有实际使用,它们也会进行优化-std=c++23。)

\n

const double DoubleBelowOne = std::nextafter(1.0, 0.);在全局范围内,最坏的情况是在启动时运行该函数一次,从而在使用它的地方击败常量传播,但在与其他运行时变量一起使用时,其性能与 FP 文字常量大致相同。

\n

  • 结果可能与“1 - ε”不同,因为 epsilon 被定义为“1.0”与下一个更大值之间的距离。(很容易误解或误用 `epsilon()`) (4认同)
  • @Andrew:如果您只是抱怨测试程序的“0.99999999999999977796”文本输出,我认为这足以转换回正确的 FP 位模式;当然这并不能完全代表;这不是 FP -&gt; 字符串函数的目的。他们的目标是在通过像“strtod”这样的函数读取时实现位精确的往返,该函数将字符串转换为双精度,并正确舍入到最接近的值。(这是[非常重要](//www.exploringbinary.com/properties-of-the- Correction-loop-in-david-gays-strtod/))。https://godbolt.org/z/9v1Gdf6zc 确认 0x1.fffffffffffffp-1 (4认同)
  • @MarekR 是的,对于 IEEE-754 格式,这可行。但是“nextafter”方法*根据定义*将为任何系统/架构/类型给出正确的答案。或者它*应该*。毫无疑问,它的实现方式将取决于目标架构所使用的系统。 (2认同)

Eri*_*hil 9

利用C标准中规定的浮点表示的特性,可以在不调用函数的情况下进行计算。由于epsilon提供了略高于 1 的可表示数字之间的距离,并radix提供了用于表示数字的基数,因此略低于 1 的可表示数字之间的距离epsilon除以该基数:

#include <iostream>
#include <limits>


int main(void)
{
    typedef float Float;

    std::cout << std::hexfloat <<
        1 - std::numeric_limits<Float>::epsilon() / std::numeric_limits<Float>::radix
        << '\n';
}
Run Code Online (Sandbox Code Playgroud)