小于 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
参数。 (通常是 -Infinity
、0
或 +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-1
和 0x1.ffffffffffffep-1
(1 - ε
)
请注意,当使用类似技术确定 大于 1
的最接近值时,nextafter(1.0, 10000.)
调用给出与1 + ε
计算 (1.00000000000000022204),正如 ε.
的定义所预期的那样
性能
C++23 要求 std::nextafter
为 constexpr
,但目前只有部分编译器支持。 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
有没有办法得到小于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
参数。 (通常是 -Infinity
、0
或 +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-1
和 0x1.ffffffffffffep-1
(1 - ε
)
请注意,当使用类似技术确定 大于 1
的最接近值时,nextafter(1.0, 10000.)
调用给出与1 + ε
计算 (1.00000000000000022204),正如 ε.
性能
C++23 要求 std::nextafter
为 constexpr
,但目前只有部分编译器支持。 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