<float.h>中DBL_TRUE_MIN的定义和作用

搬运自己2016年11月22日于SegmentFault发表的文章。链接:https://segmentfault.com/a/1190000007565915


在学习C Prime Plus的过程中遇到了这道复习题,利用搜索引擎加上自己的一些思考,初步得出了结论,如有谬误之处,还望不吝赐教😂

下列循环中,如果valuedouble类型,会出现什么问题?

for (value = 36; value > 0; value /= 2)
  printf("%3d", value);

推测运行结果:如果valuedouble类型,计算过程中会导致无限循环(直到超过double类型的精度表示范围),又因printf以整型形式打印数字,会在最后出现很多个数字0

答案中指明了这种现象的名词:浮点数下溢(underflow)

实际运行结果:

...(很多个0)
0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0-21474836481073741824536870912-1879048192120795955260397977630198
98881509949447549747237748736188743689437184471859223592961179648589824294912147
4567372836864184329216460823041152576288144 72 36 18  9  4  2  1请按任意键继续.
. .

确实出现了很多个0,但是在很多个0的后面又莫名其妙多出了一堆整数。为了分离出后面的长数字,修改格式化字符串为" %3d",结果如下:

...(很多个0)
0   0   0   0   0 -2147483648 1073741824 536870912 -1879048192 1207959552 603
979776 301989888 150994944 75497472 37748736 18874368 9437184 4718592 2359296 11
79648 589824 294912 147456 73728 36864 18432 9216 4608 2304 1152 576 288 144  72
36  18   9   4   2   1请按任意键继续. . .

为了进一步弄清楚产生这种现象的原因,继续修改格式化字符串为" %.3le",结果如下:

...
  4.663e-317 2.331e-317 1.166e-317 5.828e-318 2.914e-318 1.457e-318 7.285e-319 3
.643e-319 1.821e-319 9.107e-320 4.553e-320 2.277e-320 1.138e-320 5.692e-321 2.84
6e-321 1.423e-321 7.115e-322 3.557e-322 1.779e-322 8.893e-323 4.447e-323 1.976e-
323 9.881e-324 4.941e-324请按任意键继续. . .

可以看到,以整型输出的double类型变量实际上一直在减小,应该是double在内存中被截断读取为int之后导致了显示为整数的情况。

查看<float.h>头文件,找到两个和double类型精度有关的明示常量:

#define DBL_MIN          2.2250738585072014e-308 // min positive value
#define DBL_TRUE_MIN     4.9406564584124654e-324 // min positive value

可以看出,导致循环终止的原因是,循环中最后一个数字4.941e-324除以2之后的结果小于DBL_TRUE_MIN的值

为什么<float.h>中要采用DBL_MINDBL_TRUE_MIN两个具有相同注释的常量?我首先利用搜索引擎查到了这样一个带注释的版本:

#ifndef DBL_TRUE_MIN
/* DBL_TRUE_MIN is a common non-standard extension for the minimum denorm value
 * DBL_MIN is the minimum non-denorm value -- use that if TRUE_MIN is not defined */
#define DBL_TRUE_MIN DBL_MIN
#endif

注释部分大意为,DBL_TRUE_MIN是对最小非规格化浮点数(Denormal number)的通用非标准扩充,而DBL_MIN才是最小非规格化浮点数的值,并且只在DBL_TRUE_MIN未定义时使用。

C11标准 §5.2.4.2.2.13中提到了DBL_TRUE_MIN

The values given in the following list shall be replaced by constant expressions with implementation-defined (positive) values that are less than or equal to those shown:
— minimum normalized positive floating-point number, bemin−1
FLT_MIN 1E-37
DBL_MIN 1E-37
LDBL_MIN 1E-37
— minimum positive floating-point number
FLT_TRUE_MIN 1E-37
DBL_TRUE_MIN 1E-37
LDBL_TRUE_MIN 1E-37
(If the presence or absence of subnormal numbers is indeterminable, then the value is intended to be a positive number no greater than the minimum normalized positive number for the type.)

根据上面查到的信息,我得出了以下的结论:

首先,浮点数在计算机中有规格化数和非规格化数两种表示方式。C标准在这里规定了两种浮点数的最小正值。具体值的大小是由实现来定义的(由编译器等来决定),但是不得小于列出的这些值。并且,如果没有确定非规格化数是否会出现,DBL_TRUE_MIN的值应该是一个小于等于该类型最小正规格化数DBL_MIN的值,FLT_TRUE_MINLDBL_TRUE_MIN同理。

本来想快点看完这本书的,结果遇到一个弄不懂的问题就查了很长时间……这个其实并不是一个好习惯。不过,反正我自己已经查过了,可能有很多个人理解不正确的地方,但是如果可以帮助到其他人,我发到这里的目的就达到了😊

posted on 2018-01-08 21:10  MorrisLi  阅读(1558)  评论(0编辑  收藏  举报

导航