C++中float类型的存储
C++中float用32位来表示,f = (-1)^S * T * 2^E,S是符号位,T是尾数,E是指数
首先我们把f表示成科学计数法的形式,然后再写出其在内存中的表示,在这里T写成1.XXX的形式,所以可以表示24位尾数
举例来说 f = 14.25f = (1110.01)B = (1.11001*2^3)B 所以符号位S = 0, T = 11001B, E = 3 = 11B
另外指数可以为负数,在C++中,是把指数加上127来存储的,即E= E+ 127 = 130 = 10000010B
即
在x86系统中,小端模式,因此在内存中的存储为0x00|00|64|41
那么我们可以计算一下C++中float表示的范围了,可以先列出S,T,E的取值范围
S = 0 或者1
T = 最小0 最大 1-2^-32
S = 最小-127 最大128
绝对值最大为 ABSMAX = [1-(2^-32)]* 2^128 约等于 3.4E+38
绝对值最小为 ABSMIN = 1.0 * 2^(-127)
所以取值范围是 [-ABSMAX, -ABSMIN] 和 [ABSMIN, ABSMAX]
另外对于0.0f,内存中是以全0表示
附float与int之间的转换,首先需要说明的是int与char在C++中都是以补码形式存储
int->float
把int写成科学计数法形式,比如 i = -128 = -1.0*(2^7) B 所以符号位S = 1, T = 0B, E = 7 = 111B
E = E+ 127 = (10000110)B
在x86系统中,小端模式,因此在内存中的存储为0x00|00|00|C3,而int类型的存储为0x80|ff|ff|ff(补码形式)
注意在整个过程中int要进行右移操作,int有1位符号位,31位数字位,但是float只有24位尾数,所以可能造成精度下降
float->int
int表示的范围是[-2^31, 2^31-1],因此只是落在此范围中的float转成int有实际意义,否则结果是未可知的
float数字位f = (-1)^S * T * 2^E, 令T = 1.T,然后根据指数E对T进行移位操作即可,最后根据符号位S判断结果的正负
对于f = 0,内存中以全0表示,需要单独处理
代码如下
float f = 40; //*(int *)(&f) = 0xFFFFFF; int p = *(int *)&f; //尾数 当然与真实的尾数左移了23位 int t = (p & 0x7FFFFF) | 0x800000; //指数 int e = (p >> 23) & 0xFF; //符号位 int s = p >> 31; if(e - 127 < -31 || e - 127 > 31) { printf("结果不可知\n"); } e = e-127-23; if(e > 0)//左移 p = t << e; else if(e < 0)//右移 p = t >> -e; if(s < 0) p = -p; if(f == 0) p = 0;
在上述代码中,不管是左移还是右移当移动次数大于等于32时,会得到意想不到的结果,实际上有如下结论
如果被移动对象的长度是n位,那么移动计数必须大于等于0,并且严格小于n。
对于宽度为m的类型, 在X86上运行,移动次数为n,若n>=m,结果相当于移动 n&(m-1) 次
若n < 0, 则存在最小的k,使得k*m + n = n' > 0,相当于移动n'次