为什么将 1 添加到 numeric_limits<float>::min() 返回 1?

Why does adding 1 to numeric_limits<float>::min() return 1?

本文关键字:min gt 返回 float lt 添加 numeric limits 为什么      更新时间:2023-10-16

为什么从浮点最大值中减去 1 会返回一个合理的值,而将 1 加到浮点最小值会返回 1?

我认为,如果您添加或减去小于该特定幅度的epsilon的值,那么什么都不会发生,也不会增加或减少。

这是我使用 g++ 编译的代码,没有标志并在x86_64上运行。

#include <limits>
#include <iostream>
int main() {
float min = std::numeric_limits<float>::min() + 1;
float max = std::numeric_limits<float>::max() - 1;
std::cout << min << std::endl << max << std::endl;
return 0;
}

输出此内容:

1
3.40282e+38

我希望它输出这个:

-3.40282e+38
3.40282e+38

std::numeric_limits<float>::min()返回最小的规范化正值。若要获取没有低于该值的值,请使用std::numeric_limits<float>::lowest()

https://en.cppreference.com/w/cpp/types/numeric_limits/min

>min是最小幅度的正归一化浮点数,是一个非常小的正数(大约1.17549e-38(,而不是具有大幅度的负数。 请注意,-在指数中,这是科学记数法。e-38表示小数点后 38 个零。 在 https://www.h-schmidt.net/FloatConverter/IEEE754.html 上尝试一下,以在二进制float中播放位。

std::numeric_limits<float>::min()是最小星等归一化float,而不是-max。 CppReference甚至有一个注释,说明这可能令人惊讶。

你知道为什么选择它作为min((的值而不是最低的负值吗?对于所有其他类型来说,似乎是一个异常值。

lowestdenorm_minnumeric_limits<T>中的一些复杂功能在C++11中是新的。 大多数定义内容的选择大多遵循C。 历史上的C重视经济,并没有定义很多不同的名称。 (在古老的计算机上越小越好,而且全局命名空间中的内容也越少,这是 C 可以访问的所有内容。

浮点类型通常是1在 0 附近对称(符号/幅度表示(,因此 C 没有针对最负浮点数/双精度/长双精度的单独命名常量。 只需FLT_MAXFLT_MINCPP 宏。 C 没有模板,因此您知道何时编写 FP 代码,并在必要时对适当的常量使用-

如果您只打算使用几个命名常量,那么最有趣的三个是:

  • FLT_EPSILON告诉您可用的精度(尾数位(:nextafter(1.0, +INF) - 1.0
  • 有限浮点数的FLT_MIN/FLT_MAX分钟(归一化(和最大幅度。 这主要取决于浮点数有多少指数位。

    它们在 1.0 附近不太对称,原因有两个:FLT_MAX 中的全一尾数,以及逐渐下溢(次正态(占据最低指数场(带偏差为 0(,但FLT_MIN忽略次正态。 对于二进制 32floatFLT_MIN * FLT_MAX约为 3.99999976 IEEE754。 (出于性能原因,您通常希望避免次正态,因此您有逐渐下溢的空间,因此FLT_MIN不denorm_min是有道理的(

(有趣的事实:0.0是次正规的特例:指数场 = 0 表示尾数为 0.xxx 而不是 1.xxx(。

脚注 1:CppReference 指出,C++11std::numeric_limits<T>::lowest()可能与第 3 方 FP 类型的-max不同,但不适用于标准 C++ FP 类型。

lowest就是你想要的:最负的有限值。 它在整数和 FP 类型中是一致的,因为它是最负的值,因此例如,您可以将其用作模板化搜索循环的初始值设定项,该循环使用std::min查找数组中的最低值。

C++11还引入了denorm_min,即FP类型的最小正次正规值,即非正规值。 在IEEE754中,对象表示的所有位均为 0,但尾数低位中的 1 除外。


1.0 + 1.17549e-38(四舍五入到最接近的float后(的浮点数结果正好是1.0min低于std::numeric_limits<float>::epsilon因此,当添加到1.0时,整个更改将因舍入误差而丢失。

因此,即使您确实以全精度打印浮点数(或作为十六进制浮点数(,它也将是1.0。 但是您只是使用默认格式打印cout该格式舍入到一些有限的精度,例如 6 个十进制数字。 https://en.cppreference.com/w/cpp/io/manip/setprecision

(该问题的早期版本包括min~= 1.17549e-38的数值;这个答案开始解决这种混淆,我没有费心完全重写这些部分(。