一道C++面试题 分析浮点数的二进制表示

时间:2024-04-15 15:24:53

1、以下两条输出语句分别输出什么?

float a = 1.0f;
cout << (int)a << endl;
cout << (int&)a << endl;
cout << boolalpha << ( (int)a == (int&)a ) << endl; // 输出什么?
float b = 0.0f;
cout << (int)b << endl;
cout << (int&)b << endl;
cout << boolalpha << ( (int)b == (int&)b ) << endl; // 输出什么?

 

解析:

(int)&a :把a的地址强制转换成整型

(int&)a :把a强制转换成整形引用类型,相当于是某个整形引用,引向了a所在的32位内存区域,并将这32位当做一个整数;(int &)a的意思是将a存储单元开始的内容解释为一个int引用。

(int)a :a在内存中的值转换成int类型;(int)a 是数值意义上的转换。

分析:这个题目涉及float在计算机中的存储问题, IEEE 754的标准就是描述的这个问题。如果这个题目放在笔试的时候应该比面试的时候容易多了。

 

1、了解:

 

目前C/C++编译器标准都遵照IEEE制定的浮点数表示法来进行float,double运算。这种结构是一种科学计数法,用符号、指数和尾数来表示,底数定为2——即把一个浮点数表示为尾数乘以2的指数次方再添上符号。下面是具体的规格:

 

              符号位        阶码       尾数     长度
float           1          8        23      32
double          1         11        52      64


 

 

无论是单精度还是双精度在存储中都分为三个部分:

1.  符号位(Sign) : 0代表正,1代表为负

2.  指数位(Exponent):用于存储科学计数法中的指数数据,并且采用移位存储

3.  尾数部分(Mantissa):尾数部分

 

以单精度的浮点型为例:

指数部分(E) 占用8-bit的二进制数,可表示数值范围为0-255。 但是指数应可正可负,所以IEEE规定,此处算出的次方须减去127才是真正的指数。所以float的指数可从 -126到128.

尾数部分(M)实际是占用24-bit的一个值,由于其最高位始终为 1 ,所以最高位省去不存储,在存储中只有23-bit。

符号位:s 通过(-1)的s次幂来表示正负号。

 

 

 

以下通过几个例子讲解浮点数如何转换为二进制数

 

例一:

 

已知:整数3490593(16进制表示为0x354321)

 

求:其对应的浮点数3490593.0的二进制表示。 

 

解法如下:

 

先求出整数3490593的二进制表示:

 

 H:    3     5    4    3    2     1   (十六进制表示)

 

 B:   0011  0101 0100 0011 0010  0001 (二进制表示)

 

        │←─────  21────→│

 

 

 

即: 

 

               1.1010101000011001000012×221

 

可见,从左算起第一个121位,我们将这21为作为浮点数的小数表示,单精度浮点数float由符号位1位,指数域位k=8位,小数域位(尾数)n=23位构成,因此对上面得到的21位小数位我们还需要补上20,得到浮点数的小数域表示为:

 

         1 0101 0100 0011 0010 0001 00

 

 

 

float类型的偏置量Bias=2k-1-1=28-1-1=127,但还要补上刚才因为右移作为小数部分的21位,因此偏置量为127+21=148,就是IEEE浮点数表示标准:

 

                          V = (-1)s×M×2E

 

                          E = e-Bias

 

中的e,此前计算Bias=127,刚好验证了E=148-127=21

 

 

 

148转为二进制表示为10010100,加上符号位0,最后得到二进制浮点数表示1001010010101010000110010000100,其16进制表示为:

 

 H:     4        A       5          5         0         C         8        4  

 

 B:  0100   1010   0101    0101   0000   1100  1000   0100

 

                    |←────      21        ─────→   |

 

     1|←─8   ─→||←─────       23       ─────→ |

 

 

 

这就是浮点数3490593.0(0x4A550C84)的二进制表示。

 

例二:

 

0.5的二进制形式是0.1

 

它用浮点数的形式写出来是如下格式

 

 

 

0                01111110                 00000000000000000000000

 


符号位           阶码                       小数位

 

正数符号位为0,负数符号位为1

 

阶码是以2为底的指数

 

小数位表示小数点后面的数字

 


下面我们来分析一下0.5是如何写成0 01111110 00000000000000000000000

 


首先0.5是正数所以符号位为0

 

再来看阶码部分,0.5的二进制数是0.1,0.11.0*2^(-1),所以我们总结出来:

 

要把二进制数变成(1.f)*2^(exponent)的形式,其中exponent是指数

 

而由于阶码有正负之分所以阶码=127+exponent;

 

即阶码=127+(-1)=126  01111110

 

余下的小数位为二进制小数点后面的数字,00000000000000000000000

 


由以上分析得0.5的浮点数存储形式为0 01111110 00000000000000000000000  

 

注:如果只有小数部分,那么需要右移小数点. 比如右移3位才能放到第一个1的后面, 阶码就是127-3=124.

此处主要参考:http://blog.163.com/yql_bl/blog/static/847851692008112013117685/

2、问题的解答

对于1.0f这个数字,根据上述例子可以对进行分解整数部分和小数两部分处理,整数部分:1,它的二进制位1;小数部分为.0,它的二进制为.0。因而这个数字的二进制数为1.0。

由于1.0f为正数,因而符号位为0,即S=0;

再求阶码位: 1.0 = 1.0X2^0。即e=0,E = e + 127 = 0 + 127 = 127.转为二进制 0111 1111

再求尾数: 小数位为0,由于浮点型的尾数是23位,因而补零。M = 000 0000 0000 0000 0000 0000

最后得出浮点型1.0f的二进制表示,也就是说它的浮点数存储形式为0011 1111 1000 0000 0000 0000 0000 0000(0x3f800000)

 

(int)a :a在内存中的值转换成int类型;(int)a 是数值意义上的转换。因此(int)a 为1.

(int&)a :把a强制转换成整形引用类型,相当于是某个整形引用,引向了a所在的32位内存区域,并将这32位当做一个整数;(int &)a的意思是将a存储单元开始的内容解释为一个int引用。因此(int &)a是将它的浮点数存储内容(0x3f800000)转换成一个整型(106535216).

因而整个题目的答案为:110653532160x3f800000),false00true