为什么numeric_limits<float>::min()并不能给出最小的可能float类型数值?

10
似乎我们可以轻易地得到比numeric_limits<float>::min()更小的浮点数。为什么呢?如果numeric_limits<float>::min()不是最小的正浮点数,那它应该是什么呢?
#include <iostream>
#include <limits>
using namespace std;

int main(){
    float mind = numeric_limits<float>::min();
    float smaller_than_mind = numeric_limits<float>::min()/2;
    cout<< ( mind > smaller_than_mind && smaller_than_mind > 0 ) <<endl;
}

Run it here: https://onlinegdb.com/ry3AcxjXz

3个回答

14

min()函数返回浮点数类型的最小正值,它具有格式的完整表达能力——其尾数的所有位都可用。

更小的正值称为亚规格。虽然它们是可表示的,但尾数的高位必须为零。

IEEE-754 64位二进制浮点格式用一个符号(+或-,编码为0或1)、一个指数(-1022到+1023,编码为1到2046,加上0和2047作为特殊情况)和一个53位尾数(用52位编码加上指数字段中的提示)来表示一个数字。

对于正常值,指数字段为1到2046(表示-1022到+1023的指数),而尾数(二进制)为1.xxx…xxx,其中xxx…xxx代表52个更多的位。在所有这些值中,尾数的最低位的值是最高有效位(第一个1)的2-52倍。

对于亚规格值,指数字段为0。这仍表示-1022的指数,但意味着尾数的高位为0。现在,尾数为0.xxx…xxx。随着在此范围内使用的值越来越小,尾数的更多前导位变为零。现在,尾数的最低位的值大于最高有效位的2-52倍。在此间隔中,您不能像在正常间隔中一样精细地调整数字,因为并非尾数的所有位都可用于任意值——有些前导位被固定为0以设置比例。

由于这个原因,当处理处于此范围内的数字时,相对误差往往比正常范围内的相对误差更大。浮点格式具有此次正常范围,因为如果没有它,则数字将仅在最小正常值处截断,并且该正常值和零之间的差距将是一个巨大的相对跳跃 - 单步中值的100%。通过包括次正常数,相对误差逐渐增加,而绝对误差从此点开始保持不变,直到达到零。
了解正常范围的底部非常重要。min()会告诉你这一点。denorm_min()会告诉你最终的最小正值。

4
根据 en.cppreference.com 的说法:
对于具有非规格化形式的浮点类型,min返回最小正规范化值。请注意,这种行为可能出乎意料,特别是与整数类型的min的行为相比较时。 float 是一个具有非规格化形式的类型,关于规格化浮点数的信息,请参考此处

3

因为 numeric_limits::min 返回值是 "对于带有次正规数的浮点类型,返回最小的正规化值。" 在某些系统上,你可以将该值除以2,得到一个次正规数(在某些平台上也称为非规范数)。这些数字无法存储float类型的完整精度,但允许存储否则会变成0.0的值。


@EricPostpischil 已更新。我已经盯着英特尔文档看了太久了。 - 1201ProgramAlarm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接