简述C语言中,float类型的数据在内存中的存放

  要简单的说明浮点数在内存中的存放,可以先看下面这段代码:

#include<stdio.h>
int main()
{
    int n = 9;
    float* p = (float*)&n;
    printf("%d\n", n);
    printf("%f\n", *p);
    *p = 9.0;
    printf("%d\n", n);
    printf("%f\n", *p);
}

  它输出的结果是这样的:

9
0.000000
1091567616
9.000000

  要解释这个结果,首先我们要知道整型在内存中是怎么存放的。 int 定义的整型数据占四个字节,也就是32个比特位,有符号int第一位是符号位,后面31位是数据位,n=9,转换成二进制并补成32位就是这样的    00000000000000000000000000001001, 第一位的0表示这个数是整数,如果第一位是1就表示数据是负数,而整型的数据在内存中又存在所谓原码、反码和补码,整数的原、反、补码都是一样的,负数的反码是原码的符号位不变,其他位按位取反,补码则是反码加上一个1。这些数据都是按照补码的形式存进内存,取出时再转换成原码,按原码的数据取出。按补码存放的其中一个原因是cpu只有加法运算器,如果要计算1-1这样的式子就要转换成1+ (-1),如果按原码进行计算就会出错,按补码才能算出正确的结果。也就是说n=9,实际上在内存中存的是00000000000000000000000000001001这个二进制数。

  而float类型在内存中的存放则是按照IEEE(电气和电子工程协会) 754的国际标准进行的。根据该标准,任意一个浮点型的数据可以表示成(-1) ^ S * M * 2 ^ E。 (-1) ^ S 表示符号位,当S为0时,表示整数,S为1表示整数。M * 2 ^ E 表示要存放的数据的二进制的科学计数法,M表示有效数字,大于等于1,小于2,E表示指数位。这样的话,浮点数在内存中就是需要存放S、M、E三个二进制数。这里在32位系统和64位系统中又有差别。32位系统规定,32个比特位的第一位表示符号位,存放S,从S向后的8个比特位是指数位,存放E,剩下的23位存放有效数字M。64位系统规定,64个比特位的第一位是符号位,向后11位表示指数位,剩下的52位表示有效数字位。

  接下来这里会以32位为例进行解释。S的存放是最简单的,正数存0,负数存1。M的存放则是将整数部分的1取下,只存放小数后的数字,取出时再把1加上。因为浮点数在存放过程中,M的值就被定在了大于等于1且小于2,这就是说,M的整数部分只能是1,存放时将整数位直接取下,可以节省一个比特位给后面的小数部分。E的存放情况比较复杂,大致可以分三种情况。当E存入时不为全 0 或不为全 1 时。首先指数E在存放时,是一个无符号整数,这样E的8个比特位可以表示的整数范围就是0~255,而如果存放的浮点数指数位是负数,在存放时就会出现问题,比如0.5转换成二进制就是 0.1,即 0 * 2 ^ 0 + 1 * 2 ^ (-1)。这样写成标准规定的表示形式就是(-1) ^ 0 *  1.0 * 2 ^ (-1) ,即S = 0, M = 1.0 , E = -1, 而E在存放时是无符号整数。这时,标准规定,E在存放时需要先加上一个中间数,这个中间数在32位系统E 0~255的范围中取的是127,64位系统,E的范围是11个二进制位的无符号整数,即0~2047,中间数为1023。这样,当E= -1时,存进去的E就变成了(-1) + 127 = 126 ,126的二进制形式,取出时再减去中间数即可。当E为全0时,标准规定浮点数的指数E真实值就是1 - 127,这时表示的浮点数就是(-1) ^ S * M * 2 ^ (-126),这个值就会非常的小,M的具体数值对浮点数的原本值影响已经可以忽略了,这时就不会再补上M存入时去掉的1,此时的浮点数表示的就是从 ± 两个方向无限接近于0。当E为全1时,取出的真实值再做 2 ^ E,这个值就会非常非常的大,浮点数表示的值就是 ± 无穷大。

  既然 float 类型在存放时是这样的形式,那在以 float 类型取出时,当然也会按照这个标准进行。这样再回过去看开头那段代码,当对n = 9 ; 进行 float*p = (float*)&n的操作,再以 %f 的形式 解引用输出p,这样系统就会把存进去的 n 的补码,即00000000000000000000000000001001,以 float 类型的标准输出。第一位是符号位,即S = 0,从S向后 8 位为E,为全0,剩下的是就是M,此时M的值已经不重要了,取出的这个浮点数的值非常非常的小,而 %f 打印出来的值只能表示小数点后6位,自然就只能打印出0.000000。再看 *p = 9.0; 首先将 9.0 转换成二进制科学计数法,9.0二进制为1001.0,科学计数法就是1.001 * 2 ^ 3,小数点向左移动3位,所以是2 ^ 3。这样按照 float 的标准存放就是 (-1) ^ 0 * 1.001 * 2 ^ 3。即S = 0,M = 1.001,E = 3,存入时,S为0,M去掉整数部分的1,存入 001 ,E存入 (3 + 127) =  130 ,即130的二进制形式。

  所以存进去的二进制为  0 (S) 10000010 (E) 00100000000000000000000 (M)。这个二进制数再以%d 的形式输出整型数据,符号位为0,即表示正数,正数的原、反、补码相同,所以输出的数据就是这个二进制的十进制数,即我们开头那段代码输出的1091567616。

  以上就是对 float 类型数据在内存中存放的简要描述。

  

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值