浮点数的存储
IEEE745 国际标准:
任意一个二进制的浮点数可以表示为下面的形式:
- V = (-1)^s^ * M * 2^E^
- (-1)^s^表示符号位,当S=0的时候V为正数,当S=1的时候V为负数
- M表示有效数字,大于等于1,小于2; 1<= M <2
- 2^E^:E表示指数位
科学计数法:123.45 = 1.2345 * 10^2^
举例:
V = 5.0 f
二进制:101.0
科学计数法:1.01 * 2^2^
iEEE:(-1)^0^ * 1.01 * 2^2^
S = 0; M = 1.01; E = 2
V = 9.5f
- 二进制:1001.1
- 小数点后面的权重从左往右为2^-1^ ,2^-2^ , 2^-3^ ……表示1/2^1^, 1/2^-2^,1/2^-3^ ……即为0.5,0.25, 0.125……
- 科学计数法:1.0011 * 2^3^
- IEEE:(-1)^0^ * 1.0011*2^3^
- S = 0; M = 1.0011; E = 3
精度丢失:
- V = 9.6f
- 二进制:1001.100……(难以精确)
- float - 4byte - 32bit
- double - 8byte - 64bit
- 浮点数在内存中无法精确保存
- 对与浮点数,最高位的1位是符号位S,接着8位是指数E,剩下23位位有效数字M
- M:可以写成1.xxxxxx,在计算机内部保存时,默认M的第一位总是1,因此可以被舍去,只保留后面的xxxxx 部分,比如保存1.01时,舍去前面的1,保留后面的01,等到读取的时候再把第一位加上去,这样做的目的是节省一位有效数字,以单精点浮点数为例原本23位有效数字,在舍去一位后可以保存24位
- E:为一个无符号整数,如果E为8位,它的取值范围是0-255;如果是11位,它的取值范围是0-2047。但是科学计数法是可以出现负数的(0.5 = 0.1 * 2^-1^ ),因此规定,存入时E的真实值必须再加上一个中间数,对于8位的E,这个中间数是127;对于11位的E这个中间数是1023,存储值 = E(真实值) + 127(float) / 1023(double) (中间值)
- 比如:2^10^ 的E时10,所以保存成32位浮点数时,必须保存位10+127=137,即10001001
#include<stdio.h>
int main(){
float f = 5.5;
//5.5
//101.1
//1.011 * 2^2 ----科学计数法
//S=0; M=1.011; E=2
//0 ----符号位
//2 + 127 = 129 ----有效数字
//129 = 1000 0001 ----二进制
//011 ----指数
//指数位为23位,不够补零
//011 0000000000 0000000000
//内存存储:0 10000001 01100000000000000000000 ---二进制
//内存存储:0100 0000 1011 0000 0000 0000 0000 0000 = 0x40 b0 00 00 ---十六进制
return 0;
}
//输出 40 b0 00 00
取出:
E的三种情况
E不全为0或不全为1时,既有0又有1
指数的E减去127(或1023),得到真实值,再将有效数字M得前面加上第一位的1
比如0.5(1/2)的二进制形式为0.1,由于规定正数必须为1,即将小数右移一位,则为1.0 * 2^-1^ 其阶码为 -1+127 = 126表示为01111110,而尾数1.0去掉整数部分为0,补齐到23位00000000000000000000000,则二进制表示为:
0 011111110 00000000000000000000000
E全为0
- 浮点数的指数E等于1-127(或1-1023)即为真实值
- 有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数,这样做是为了表示+-0,以及接近0的很小的数字
E全为1
- 这是,如果有效数字M全为1,表示+-无穷大,正负值取决上符号位的S
#include<stdio.h>
int main(){
int n = 9;
// 00000000 00000000 00000000 00001001---整数的原码,反码,补码相同
//
float* pFloat = (float*)&n;
printf("n为:%d\n",n);//9
printf("n为:%f\n",*pFloat);// 0.000000无限接近于0的数
// 0 0000000 00000000 00000000 00001001---
// E= -126 \
// M = 0.000000000000000000000000001001
// +0.000000000000000000000000001001 * 2^-126
//
*pFloat = 9.0;
//1001.0
// 1.001 * 2^3
// S = 0; E = 3; M = 1.001
//0 10000010 00000000000000000000000 --- 内存存储
printf("num为:%d", n); //1091567616
// +1091567616 (正数原码补码相同)
//01000001000000000000000000000000
printf("*pFloat为:",*pFloat);//9.0
}