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

Largest value representable by a floating-point type smaller than 1

有没有办法得到小于1.

的浮点型float可表示的最大值

seen the following definition:

static const double DoubleOneMinusEpsilon = 0x1.fffffffffffffp-1;
static const float FloatOneMinusEpsilon = 0x1.fffffep-1;

但这真的是我们定义这些值的方式吗?

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

您可以使用 std::nextafter function,尽管它的名称如此,但它可以通过使用适当的to 参数。 (通常是 -Infinity0+Infinity)。

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

示例:为 double 类型检索小于 1 的最接近值(在 Windows 上,使用 Visual Studio 2019 中的 clang-cl 编译器),答案是不同于 1 - ε 计算的结果(正如评论中所讨论的那样,对于 IEEE754 数字是不正确的;低于 2 的任何幂,可表示的数字是上面的两倍):

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

int main()
{
    double naft = std::nextafter(1.0, 0.0);
    std::cout << std::fixed << std::setprecision(20);
    std::cout << naft << '\n';
    double neps = 1.0 - std::numeric_limits<double>::epsilon();
    std::cout << neps << '\n';
    return 0;
}

输出:

0.99999999999999988898
0.99999999999999977796

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


请注意,当使用类似技术确定 大于 1 的最接近值时,nextafter(1.0, 10000.) 调用给出与1 + ε 计算 (1.00000000000000022204),正如 ε.

的定义所预期的那样

性能

C++23 要求 std::nextafterconstexpr,但目前只有部分编译器支持。 GCC 确实通过它 constant-propagation,但 clang 不能 (Godbolt)。如果你希望它像 0x1.fffffffffffffp-1; 这样的文字常量一样快(启用优化)对于 double 是 IEEE754 binary64 的系统,在某些编译器上你必须等待 C + 的那部分+23 支持。 (很可能一旦编译器能够做到这一点,就像 GCC 一样,即使不实际使用 -std=c++23,它们也会进行优化。)

const double DoubleBelowOne = std::nextafter(1.0, 0.); 在全局范围内最坏情况下 运行 该函数会在启动时执行一次,在使用它的地方阻止常量传播,但在与其他 运行时间变量。

利用C标准规定的floating-point表示的特性,无需调用函数即可计算出。由于 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';
}

0.999999940395355224609375 是小于 1 的最大 32 位浮点数。下面的代码对此进行了演示:

Mac_3.2.57$cat float2uintTest4.c 
#include <stdio.h>
int main(void){
    union{
        float f;
        unsigned int i;
    } u;
    //u.f=0.9999;
    //printf("as hex: %x\n", u.i); // 0x3f7fffff
    u.i=0x3f800000; // 1.0
    printf("as float: %200.200f\n", u.f);
    u.i=0x3f7fffff; // 1.0-e
          //00111111 01111111 11111111 11111111
          //seeeeeee emmmmmmm mmmmmmmm mmmmmmmm
    printf("as float: %200.200f\n", u.f);

    return(0);
}
Mac_3.2.57$cc float2uintTest4.c 
Mac_3.2.57$./a.out 
as float: 1.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
as float: 0.99999994039535522460937500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000