是否可以在不读取float.h和使用ANSI C的情况下以可移植的方式计算float,double和long double数据类型的范围?便携式,我的意思是包括目标机器不符合IEEE 754标准的情况.
我正在阅读K&R书,练习2-1要求我"计算"它们,所以我想这意味着完全避免使用float.h,其中包括FLT_MIN,FLT_MAX,DBL_MIN和DBL_MAX(直接读取这些值肯定不会归类为"计算" ").
通过(伪代码)计算最大浮点值是可能的(至少对于IEEE 754 float
和double
值):
~(-1.0) | 0.5
在我们完成比特之前,我们必须将浮点值转换为整数然后再转换回来.这可以通过以下方式完成:
uint64_t m_one, half; double max; *(double *)(void *)&m_one = -1.0; *(double *)(void *)&half = 0.5; *(uint64_t *)(void *)&max = ~m_one | half;
那么它是怎样工作的?为此,我们必须知道如何编码浮点值.
最高位对符号k
进行编码,下一位编码指数,最低位将保持小数部分.对于权力2
,分数部分是0
.
指数将以偏差(偏移)存储2**(k-1) - 1
,这意味着指数0
对应于具有除最高位集之外的所有位置的模式.
有两个具有特殊含义的指数位模式:
如果没有设置位,则该值将为0
小数部分为零; 否则,该值是次正规值
如果设置了所有位,则值为infinity
或NaN
这意味着最大的正则指数将通过设置除最低位之外的所有位进行编码,这对应于2**k - 2
或者2**(k-1) - 1
减去偏差的值.
对于double
值,k = 11
即最高指数将是1023
,因此最大浮点值2**1023
是约为的阶1E+308
.
最大的价值就是
符号位设置为 0
除最低指数位之外的所有位都设置为 1
所有小数位设置为 1
现在,我们可以理解我们的幻数如何工作:
-1.0
它的符号位设置,指数是偏差 - 即所有位但存在最高位 - 并且小数部分是 0
~(-1.0)
只有最高指数位和所有小数位设置
0.5
有一个符号位和小数部分0
; 指数将是偏差减去1
,即除最高和最低指数位之外的所有指数都将存在
当我们通过逻辑组合这两个值时,我们将得到我们想要的位模式.
该计算也适用于x86 80位扩展精度值(也称为long double
),但是由于没有足够大的整数类型来保存32位硬件上的值,因此必须按字节顺序完成.
实际上并不需要偏差2**(k-1) - 1
- 只要它是奇数,它就可以用于任意偏差.偏差必须是奇数,否则指数的位模式1.0
和0.5
其他地方的位模式将不同于最低位.
如果b
浮点类型的基数(又名基数)不是2
,则必须使用b**(-1)
而不是0.5 = 2**(-1)
.
如果未重新保留最大指数值,请使用1.0
而不是0.5
.这将无论基数还是偏差都有效(意味着它不再局限于奇数值).使用的不同之1.0
处在于不会清除最低指数位.
总结一下:
~(-1.0) | 0.5
只要基数为2
,则偏差为奇数,最高指数为保留.
~(-1.0) | 1.0
只要不保留最高指数,就适用于任何基数或偏差.