直接计算的最小正浮点值与<float.h>中的FLT_MIN不同

Sha*_*ang 18 c floating-point

我想直接计算该float类型的最小值,这是我的算法(假设浮点数的编码符合IEEE 754标准):

#include <math.h>
#include <limits.h>
#include <float.h>
#include <stdio.h>

float float_min()
{
    int exp_bit = CHAR_BIT * sizeof(float) - FLT_MANT_DIG;
    float exp = 2 - pow(2, exp_bit - 1);

    float m = pow(2, -(FLT_MANT_DIG - 1));

    return m * pow(2, exp);
}

int main()
{
    printf("%g\n", float_min());
}
Run Code Online (Sandbox Code Playgroud)

输出是1.4013e-45.不过,我觉得值了FLT_MINC:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\float.h1.175494351e-38F.谁错了?

Pet*_*ker 41

虽然此问题已被多次提出并回答,但我认为没有任何答案是正确的.关键是这FLT_MIN是可以表示的最小标准化值.回到过去那些重要的事情.然后英特尔出现并引入了次正规值,这会降低精度,以便表示接近0的值.次正规是具有最小指数的值和高位全为零的分数.由此得出,最小的非零次正规值除了最低位(即1)之外都有一个全零的分数.这是可以表示的最小值,但是当你在那里时,在这里改变一下并且价值发生了很大的变化,因此必须非常小心地使用这些东西.

编辑,澄清"规范化":

假设我们正在写小数值:6.02x10 ^ 23,.602*10 ^ 24,60.2*10 ^ 22.这些都代表相同的价值,但它们显然看起来不同.因此,让我们引入一个写十​​进制值的规则:每个值必须在小数点左边只有一个非零数字.因此,该值的"标准化"形式为6.02x10 ^ 23,如果我们有一个以非标准化形式写的值,我们可以移动小数点并调整指数以保留该值并将其置于标准化形式.

IEEE浮点执行相同的操作:规则是分数的高位必须始终为1,并且任何计算都必须调整其结果的分数和指数以满足该规则.

当我们编写非常接近0的十进制值时,这不是问题:我们可以使指数尽可能小,因此我们可以编写类似6.02*10 ^ -16384的数字.对于浮点值,我们不能这样做:有一个我们不能低于的最小指数.为了允许更小的值,IEEE要求说当指数是最小的可表示值时,分数不必被标准化,也就是说,它不必在其高位中具有1.在写十进制值时,就像说我们可以在小数点左边有一个0.因此,如果我们的十进制规则表示最低允许指数为-100,则最小标准化值为1.00x10 ^ -100,但较小值可表示为非标准化:0.10*10 ^ -100,0.01*10 ^ - 100等

现在为我们的十进制规则添加一个要求,我们只能有三个数字:小数点左边一个,右边两个.这就像浮点部分,因为它具有固定的位数.因此,对于小的正常值,我们有三个数字可以使用:1.23*10 ^ -100.对于较小的值,我们使用前导零,其余数字的精度较低:0.12*10 ^ -100有两位数,0.01*10 ^ -100只有1.这也是浮点次正规的工作方式:你得到更少和当你越远越低于最小标准化值时,有效位越少,直到你用完了比特并得到0.

编辑:为了澄清术语,IEEE-754标准提到那些大于0且小于最小标准化值的值作为非正规数 ; 最新版本的IEEE-754将它们称为次正规.他们的意思是一样的.

  • @Ruslan William Kahan在KCS选秀中介绍了法官(与Jerome Coonen和Harold Stone一起).当时Kahan是英特尔公司设计8087的顾问.有人讲述了这个故事:https://blogs.mathworks.com/cleve/2014/07/21/floating-point-denormals-insignificant-但争议-2 / (11认同)
  • 是否引用了反正义的英特尔?我好像找不到一个. (3认同)

ikl*_*kov 16

你的结果1.4013e-45是非正规的最小正浮点值,也称为FLT_TRUE_MIN等于1.401298464e-45F.

FLT_MIN是规范化的最小正浮点值(1.175494351e-38F)