是否可以在不读取float.h和使用ANSI C的情况下以可移植的方式计算float,double和long double数据类型的范围?便携式,我的意思是包括目标机器不符合IEEE 754标准的情况。
我正在阅读K& R书,练习2-1要求我“计算”它们,所以我想这意味着完全避免使用float.h,其中包括FLT_MIN,FLT_MAX,DBL_MIN和DBL_MAX(直接读取这些值肯定会不归类为“计算”)。
答案 0 :(得分:11)
通过(伪代码)计算最大浮点值是可能的(至少对于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
适用于任何基数或偏差,只要不保留最高指数。
答案 1 :(得分:4)
对于99.99%的所有应用程序,您应该假设IEEE 754并使用<float.h>
中定义的常量。在另外0.01%的情况下,您将使用非常专业的硬件,在这种情况下,您应该知道基于硬件使用什么。
答案 2 :(得分:3)
冒着多余的回答:
没有。没有可移植的方法来计算范围。这就是提供<float.h>
标头的原因 - 因为没有可移植的方法来获取其中包含的信息。
答案 3 :(得分:2)
你可以尝试将浮子放大,直到它溢出。