提问人:0xbadf00d 提问时间:3/7/2022 最后编辑:Adrian Mole0xbadf00d 更新时间:3/9/2022 访问量:2147
可由小于 1 的浮点类型表示的最大值
Largest value representable by a floating-point type smaller than 1
问:
有没有办法获得由小于 的浮点类型表示的最大值。float
1
我看到了以下定义:
static const double DoubleOneMinusEpsilon = 0x1.fffffffffffffp-1;
static const float FloatOneMinusEpsilon = 0x1.fffffep-1;
但这真的是我们应该如何定义这些价值观吗?
根据该标准,是机器epsilon,即1.0与下一个值之间的差值,可由浮点类型表示。但这并不一定意味着定义会更好。std::numeric_limits<T>::epsilon
T
T(1) - std::numeric_limits<T>::epsilon
答:
您可以使用 std::nextafter
函数,尽管它的名称,但它可以通过使用适当的参数来检索给定起点之前的下一个可表示值。(通常为 、 或 )。to
-Infinity
0
+Infinity
根据 的定义,无论 C++ 实现使用哪种浮点格式,这都可以移植。(二进制与十进制,或尾数的宽度,又名 significand,或其他任何东西。nextafter
示例:检索类型的最接近值小于 1(在 Windows 上,使用 Visual Studio 2019 中的 clang-cl 编译器),答案与计算结果不同(如注释中所述,对于IEEE754数字不正确;低于 2 的任意幂,可表示数字之间的接近程度是上面的两倍):double
1 - ε
#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-1
0x1.ffffffffffffep-1
(1 - ε
)
请注意,当使用类似技术确定大于 的最接近值时,调用将给出与计算相同的值 (1.000000000000000022204),正如 ε 的定义所预期的那样。1
nextafter(1.0, 10000.)
1 + ε
性能
C++23 要求 ,但目前只有一些编译器支持。GCC 确实通过它进行持续传播,但 clang 不能 (Godbolt)。如果您希望它像IEEE754 binary64 的系统一样快(启用优化),那么在某些编译器上,您必须等待 C++23 支持的那部分。(很有可能,一旦编译器能够做到这一点,就像GCC一样,即使没有实际使用,它们也会进行优化。std::nextafter
constexpr
0x1.fffffffffffffp-1;
double
-std=c++23
const double DoubleBelowOne = std::nextafter(1.0, 0.);
在全局范围内,最坏的情况是在启动时运行一次函数,在使用它的地方破坏常量传播,但在与其他运行时变量一起使用时,其性能与 FP 文本常量大致相同。
评论
1 - ε
1.0
epsilon()
)
long double
std::numeric_limits<double>::epsilon();
给出 1.0 和下一个最小值之间的差值。因此,要获得比 1.0 更小的东西,应该提取 epsilon 的一半,因为与 相比,该值的指数小 1 。所以它应该是:1.0
double neps = 1.0 - std::numeric_limits<double>::epsilon() * 0.5;
nextafter
0.99999999999999977796
strtod
这可以通过使用 C 标准中指定的浮点表示特征来计算,而无需调用函数。由于 提供刚好高于 1 的可表示数字之间的距离,并提供用于表示数字的基数,因此刚好低于 1 的可表示数字之间的距离除以该基数:epsilon
radix
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';
}
评论
epsilon()
FLT_EPSILON
DBL_EPSILON
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
评论
float
0.99999994039535522461
0.99999988079071044922
float
float
评论
std::nextafter
?1.0 - DBL_EPSILON / 2