c语言浮点数(float double)内存存储方式------移位存储

文章目录

    • 一、概述
    • 二、转换示例
    • 三、基于转换误差的思考
    • 四、为什么float有些时候表达的int数据不准确

一、概述

C语言中,对于浮点类型的数据采用单精度类型(float)和双精度类型(double)来存储,float数据占用32bit,double数据占用64bit, float遵从的是IEEE R32.24,而double 遵从的是R64.53。
Float类型变量占4个字节,也就是32bit。单精度浮点值格式:1位符号位,8位指数,23位小数。
c语言浮点数(float double)内存存储方式------移位存储_第1张图片
具体如下:
二进制浮点数可以表示成:Float=(-1)s x M x2e
1、 符号位(sign):0代表正,1代表负。
2、 指数位(exponent):用于存储科学计数法中的指数数据,并且采用移位存储。
指数E分成三种情况:
(a)E不全为0或不全为1。指数E的计算值减去127,得到真实值,再将有效数字M前加上第一位的1。
(b)E全为0。浮点数的指数E等于1-127(或者1-1023),有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数。这样做是为了表示±0,以及接近于0的很小的数字。
(c)E全为1。这时,如果有效数字M全为0,表示±无穷大(正负取决于符号位s);如果有效数字M不全为0,表示这个数不是一个数。
3、 尾数部分(mantissa):位数部分

二进制科学计数法第一位都是1,可以将小数点前面的1省略,所以23bit的位数部分可以表示精度为24bit。
double类型占八个字节。双精度是1位符号,11位指数,52位小数。内存示例如下:
c语言浮点数(float double)内存存储方式------移位存储_第2张图片

二、转换示例

下面讨论一下十进制数与二进制数的转换方法:

十进制:0.15625转换为二进制表示的过程如下:
整数位:0
小数:0.15625
将小数转换位二进制的方法就是乘二,取整数部分依次从左往右放在小数点后,直至小数点后为0。
0.15625x2=0.31250  取0
0.3125x2=0.6250 取0
0.625x2=1.250 取1
0.25x2=0.5 取0
0.5x2=1 取0
(0.00101)2=1.01*2^(-3)
符号位:0,表示位正数
指数位:-3+127=124 表示为二进制数就是01111100
尾数部分:1.01-1=0.01,后面不够23位的补0也就是补21个零
即0.15625表示为内存中二进制数的形式为:
0  01111100  01000000000000000000000

0  01111100  01000000000000000000000
表示的数转换为十进制表示过程为:
符号位:0,表示位正
指数位:01111100表示的十进制为:64+32+16+8+4=124再减127=-3
尾数部分:0.01 加1表示:1+0*2^(-1)+1*2^(-2)=1.25
原数为:2^(-3)*1.25=0.15625

三、基于转换误差的思考

最近偶然看到一段程序,,发现了int与float转换时存在的问题,于是查询了相关的数据:

#include
int main(){
	int a=9;
	float *p;
	p=&a;
	printf("a数值为:%d\n",a);
	printf("float形式为:%f\n",*p);
	*p=9.0;
	printf("a数值为:%d\n",a);
	printf("float形式为:%f\n",*p);
	return 0;
}

结果令我很是不解:

a数值为:9
float形式为:0.000000
a数值为:1091567616
float形式为:9.000000

为什么0x00000009还原成浮点数就成了0.000000?
首先,将0x00000009拆分,因为是正数所以得到第一位符号位s=0,后面8位的指数E=00000000,最后23位的有效数字M=000 0000 0000 0000 0000 1001。
由于指数E全为0,所以符合第二种情况。因此,浮点数就写成:
  (-1)0×0.00000000000000000001001×2(-126)=1.001×2(-146)
显然是一个很小的接近于0的正数,所以用十进制小数表示就是0.000000。
浮点数9.0,怎么用二进制表示?怎么还原成十进制呢?
首先,浮点数9.0等于二进制的1001.0,即1.001×2^3。
那么,第一位的符号位s=0,有效数字M等于001后面再加20个0,凑满23位,指数E等于3+127=130,即10000010。
所以,写成二进制形式,应该是S+E+M,即0 10000010 001 0000 0000 0000 0000 0000。这个32位的二进制数,还原成十进制,是1091567616。

四、为什么float有些时候表达的int数据不准确

因为int与float同样占4个字节,float表示的范围又比int大并且还包含很多小数,那int的每个值都能被float表示就是不可能的事情了。
尾数决定了浮点数的精度,尾数只有23位,加上省略的那位就是24位。如果一个int类型的值小于224,那么float是完全可以表示的。如果int类型大于224就不一定能表示了。假如一个int数值的二进制表示形式是1000000000000000000000001,表示成指数形式是1.000000000000000000000001*224,对应的float的类型尾数位是000000000000000000000001一共24位,这样就完全超出了float最多容纳23位尾数的能力。所以就不能正确表达这个int值了。由此也可以得出不能被float准确表达的最小int值是224+1。我们再将1000000000000000000000001的值加1,变成了1000000000000000000000010,这样变换为指数形式可以看出尾数又变为了23位。
也就是说25位的二进制整数最后一位是0才能被float准确表示,每2个数就有一个不能被准确表示。如果是26位的二进制整数最后两位都是0才可以被float准确表达,每4个数就有3个不能被准确表示,
即,如果是n(n>23且n为整数)位二进制数,最后n-23-1位全为0是才可以被准确表示,每2(n-23-1)个数就有2(n-23)-1位不能被准确描述。

参考教程:
c变量|菜鸟教程
c语言单精度和双精度存储方式以及移位存储的优点
浮点数的秘密

你可能感兴趣的:(c)