<float.h>中DBL_TRUE_MIN的定义和作用
搬运自己2016年11月22日于SegmentFault发表的文章。链接:https://segmentfault.com/a/1190000007565915
在学习C Prime Plus的过程中遇到了这道复习题,利用搜索引擎加上自己的一些思考,初步得出了结论,如有谬误之处,还望不吝赐教😂
下列循环中,如果value
是double
类型,会出现什么问题?
for (value = 36; value > 0; value /= 2)
printf("%3d", value);
推测运行结果:如果value
是double
类型,计算过程中会导致无限循环(直到超过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_MIN
和DBL_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_MIN
和LDBL_TRUE_MIN
同理。
本来想快点看完这本书的,结果遇到一个弄不懂的问题就查了很长时间……这个其实并不是一个好习惯。不过,反正我自己已经查过了,可能有很多个人理解不正确的地方,但是如果可以帮助到其他人,我发到这里的目的就达到了😊