我想以编程方式确定为任何实际类型分配了多少位用于尾数,以及多少位用于指数。为此,我编写了以下代码(其中,符号下的位与尾数分开考虑,因此数字少 1):
https://ideone.com/YuIWNc - C 代码(float、double、long double)
https://ideone.com/342B4S - C++ 代码(float、double、long double)
https://ideone.com/VURQnw - C++ 代码(float、double、long double、__float128)
#include <cstdio>
template <typename typed> void count(unsigned *result_m, unsigned *result_e)
{
typed x = 1, exp;
unsigned res, e;
for (res=0; x!=0; ++res) x/=2;
for (exp=1,e=0; exp*2<res; ++e) exp*=2;
*result_e = e+1;
*result_m = res-exp+1;
}
int main(void)
{
unsigned f_m, f_e, d_m, d_e, ld_m, ld_e, f128_m, f128_e;
count<float>(&f_m, &f_e);
count<double>(&d_m, &d_e);
count<long double>(&ld_m, &ld_e);
count<__float128>(&f128_m, &f128_e);
printf(" S M E SZ\n");
printf("float: 1 %3u %2u %3u\n", f_m, f_e, 8 * sizeof(float));
printf("double: 1 %3u %2u %3u\n", d_m, d_e, 8 * sizeof(double));
printf("long double: 1 %3u %2u %3u\n", ld_m, ld_e, 8 * sizeof(long double));
printf("__float128: 1 %3u %2u %3u\n", f128_m, f128_e, 8 * sizeof(__float128));
}
结果是这样的:
S M E SZ
float: 1 23 8 32
double: 1 52 11 64
long double: 1 63 15 128
__float128: 1 112 15 128
对于float,double甚至__float128一切都有效(维基百科,IEEE 754-2008)。
但存在long double以下问题:
1+63+15 = 79- 79 位。而不是80。另一个位在哪里?long double表示 10 字节的数字,但sizeof返回 16。
我怎样才能得到 10?
由于在 x86 平台上,80 位浮点值与 32 位和 64 位 IEEE754 浮点值(
float和double)在表示上存在一个根本区别,因此丢失了一位。float并在尾数中double使用具有隐式前导单元的表示。也就是说,在归一化表示中,尾数中的最高单元没有显式存储,而只是隐含存储。但是在扩展的 80 位浮点型long double中,尾数中的这个前导单元总是显式存储的。因此,存在差异。
对于 and 类型
float,double您的第一个循环首先遍历数字的规范化表示,其中显式尾数始终为零,并且指数从其最大值 (127forfloat) 的一半减小到值1:之后,您的循环继续遍历数字的非规范化表示,其中指数为
0,并且尾数向右移动一个单独的单位。当这个单独的单元飞过尾数的右边缘时,它x变为零并且循环结束请注意,尾数中的 in
float和doubleone 仅出现在第一个非规范化值中,并贯穿尾数的所有位。事实证明,这种情况下非规范化的非零值的数量等于尾数中的位数。然而,当使用时,
long double尾数中最高位的那个显然从一开始就一直存在。当long double您的循环中的指数达到零并且循环开始计算非规范化值long double时,尾数中的单位不会“突然出现”到尾数的最高位置(就像它在float和中一样double),而是已经最初出现在最高位置并从那里“开始”。正因为如此,计算非规范化值的循环部分少了一次迭代。顺便说一句,总结的奇怪方式——
res指数范围的一半和尾数的宽度是充满问题的。然后,您计算一个值log2 res并期望该值正确描述指数中的位数。但是,如果在某些假设的浮点类型中尾数非常宽,则该值log2 res可能是错误的。C/C++ 帮助提供了惊人的细节:
事实证明,实现的责任完全在于编译器。
要检查去哪里和哪个位,您可以在“发现”所有位的过程中添加打印输出:
因此,例如,对于 32 位数字:
钻头消失的地方 - 有一个障碍。同样,所有未使用的位都将可见,这样您就可以估计类型的有效大小。
长双的延续:
因此,根据关于扩展精度的英文维基百科,对于正常数字,一位确实总是设置为 1 - 并且在非规范化后变为 0。在“标准”32 位和 64 位表示中,该位被省略