最近开始复习基础知识,发现才看到浮点数就一大堆疑问,上网搜了一大堆不是这个错就是那个错,上机一验证发现都有问题,干脆自己整理整理这方面知识,因为是用代码验证的,所以又涉及到与位操作符和移位操作符的内容。
Visual C++中,float的32位这样分: 符号位(S):1位 阶码(E):8位 尾数(M):23位
其中符号位就是正负号(float和double都是不能和unsigned合用的,所以一定有正负)。
先介绍尾数,尾数是一个1.MMMMMMM……(23个M),所以尾数是一个>=1&&<=2的数的小数部分,翻译成尾数还是很恰当的。
我们可以把小数看成 V=+/- 1.MMMMMMM……(23个M) * (2^E’) 这种形式,那阶码的意义就出来了,是2的幂次,因为有8位,所以能够表示[0,255],为了表示负数,这里采用移码方式(而不是整数中的补码方式,理由还没想清楚),即实际的2^E’的E’是阶码E-127,即范围变成[-127,128](很多资料上说[-128,127]是错误的),直观的说E'表示1.MMMMMMM……小数点的移位,正数则小数点向右移,负数小数点向左移。
用一段输出float内存值的代码来验证下:
#include <iostream> #include <string> using namespace std; int main(){ float f = -3.0f; unsigned int* pa = (unsigned int*)(&f); for(int i = 31;i >= 0;i--) cout << (*pa>>i & 0x01) << (i==31||i==23? "- ": " "); cout << "\n "; return 0; }
-3.0=- 1.1000000(一共22个0) * (2^1) (1.1在十进制中表示1.5,E'=1 得到E=128=10000000)
写这段验证代码的时候顺便复习了下位与操作符(&)和移位操作符(<< >>),以前完全没注意啊啊啊啊啊啊啊啊
(1)float、double、long double等类型不允许直接进行位与操作符啊,可用间接的方法变通,如float取地址(也是&符号)转换为unsigned int类型,再用取值操作符(*),这样编译器会以为是unsigned int类型。
(2)使用int、short、long移位时最好加上unsigned,这样就是汇编中逻辑移位(即全部移位),如果不加unsigned情况就较为复杂,正数全是逻辑移位,负数左移时保持符号位为1、右边补0,负数右移时保持符号位为1,左边补1,所以-1不管怎么右移都是-1。
(3)位与操作符就是将两个数进行与操作,&0x01就相当于掩码取出最后一位,其他位置成0
(4)位与操作符&的操作优先级小于移位操作符,但移位操作符小于取地址操作符&(取值操作符*),所以上面代码不会出错