当前位置:   article > 正文

浮点数存储方式理解,浮点数和整数之间的转换_f32浮点数

f32浮点数

目录

一:单精度和双精度浮点数存储格式:

二:浮点数转换成二进制表示

I:整数部分转换二进制的方法:

II:小数部分转换二进制的方法:


一:单精度和双精度浮点数存储格式:

按IEEE754标准,32位浮点数(float)和64位浮点数(double)的标准格式如下:

f = (-1)^S * (1.M) *2^e 其中:e=E-127或1023

S:符号位, 0:表示正数, 1:表示负数
E:阶码,可以理解为将一个浮点数的整数部分转换成二进制之后, 然后将该二进制数移位成1.M的形式, E为移动成1.M所需的次数e+固定偏移。在IEEE754标准中将浮点数的指数真值e变成阶码E时,需要加上固定偏移:单精度固定偏移:127,双精度固定偏移:1023
单精度(float)中E=e+127(2^(8-1)-1),双精度(double)中E=e+1023(2^(11-1)-1)
M:位数, 即将浮点数的整数部分和小数部分转换成二进制之后,然后通过移位转换成1.M的形式之后的M值,是一个纯小数部分。
在IEEE754标准中,一个单精度的32位浮点数f32的真值表示为:
e=E-127, f32 = (-1)^S * (1.M) * 2^e
在IEEE754标准中,一个双精度的64位浮点数f64的真值表示为:
e=E-1023, f64 = (-1)^S * (1.M) * 2^e
其中尾数域所表示的值是1.M。由于规格化的浮点数的尾数域最左位(最高有效位)总是1,故这一位经常不予存储,而认为隐藏在小数点的左边,取数的时候在尾数前加1。单精度时:用23位字段可以存储24位有效数。双精度时:用52位字段可以存储53位有效数。

二:浮点数转换成二进制表示

I:整数部分转换二进制的方法:

除2取余,商继续除2取余,直到商为0。读数:从最后一个余数读起,一直读到最前面的余数。

II:小数部分转换二进制的方法:

乘2取整,取完整数后剩余的小数部分继续乘2取整,一直取到小数部分为0为止。如果一直不为0,按照要求保留小数位数,根据保留位数后一位是0还是1取舍,是0舍去,是1入位。读数:从前面的整数读取到后面的整数部分。

例1:将11.375转换成IEEE754标准的32位浮点数的二进制存储格式。
解:整数:11
第一步:11 / 2 = 5余1
第二步:5   / 2 = 2余1
第三步:2   / 2 = 1余0
第四步:1   / 2 = 0余1
读数:(11)10 = (1011)2

小数部分:0.375
第一步:0.375 * 2 = 0.750取整0,小数部分0.750
第二步:0.750 * 2 = 1.500取整1,小数部分0.500
第三步:0.500 * 2 = 1.000取整1,小数部分0
读数:小数部分:(0.375)10=(0.011)2

综合:(11.375)10=(1011.011)2
转换尾数:(11.375)10=(1011.011)2 = 1.M = 1.011011 * 2 ^3
所以:M=01101100000000000000000,e=3, E=e+127=(130)10=(10000010)2
S=0:所以11.375在计算机中的存储格式为:

0 10000010 01101100000000000000000
求取计算机内存存储的单精度浮点数【0 10000010 01101100000000000000000】的十进制
f32=(-1)^S*(1.M)*2^e = (-1)^0 * (1.01101100000000000000000) * 2^3 = 1 * (1011.011)2
=(11.375)10

特别注意:这里是浮点数二进制计算,不要和十进制浮点数计算搞混,千万不要理解成1.011011 * 2^3 = 1.011011 * 8,这里的2^3是移位阶码e,不是十进制中的幂次方。

C语言验证:

void print_binary(void *addr, size_t size)
{
        unsigned char* ptr =  (unsigned char* )addr;
        char out[256] = {0};
        int len = 0;
        printf("Hex: ");
        for(size_t byte_idx = size; byte_idx > 0; --byte_idx)
        {
                unsigned char byte = *(ptr + byte_idx - 1);
                printf("%02x ", byte);
                for(char bit_idx = 7; bit_idx >= 0; --bit_idx)
                {
                        int bit = (byte >> bit_idx) & 1;
                        len += snprintf(out + len, sizeof(out) - len - 1, "%d", bit);
                }
                len += snprintf(out + len, sizeof(out) - len - 1, " ");
        }
        printf("\nbinary:%s\n", out);
}

int main(int argc, char *argv[])
{
        float f32 = 11.375f;
        print_binary(&f32, sizeof(f32));
        return 0;
}

输出:

Hex: 41 36 00 00
binary:01000001 00110110 00000000 00000000

 和理论计算一致:

例2:理解以下代码打印结果为什么是:f32=100000000.000000,小数点后的数为什么不见了?

int main(int argc, char *argv[])
{
        float f32 = 100000000.1f;
        printf("f32=%f\n", f32);
}

代码执行结果:

f32=100000000.000000

第一步:将(100000000.1)10转换为单精度二进制

整数部分(100000000)10,转换为二进制:
(100000000)10 = (0101111101011110000100000000)2
小数部分:(0.1)10转换二进制
(0.1)10=(0.00011)2
(100000000.1)10=(-1)^0*(1.0111110101111000010000000000011) * 2^26

第二步:从第一步可知

S=0,M=0111110101111000010000000000011,
e=26, E=e+127=(10011001)2

第三步:内存中理论存储

0 10011001 01111101011110000100000,由于单精度M只能存储23位,所以 M中的00000011被丢弃,所以最终内存中存储的二进制为:
0 10011001 01111101 01111000 0100000

结论:浮点数的整数部分过大,导致小数部分精度丢失

C语言验证:

int main(int argc, char *argv[])
{
        float f32 = 100000000.1f;
        printf("f32=%f\n", f32);
        print_binary(&f32, sizeof(f32));
        return 0;
}

结果:

f32=100000000.000000
Hex: 4c be bc 20
binary:0 10011001 01111101 01111000 0100000

验证结果和理论分析结果一致

例3:将例2中的单精度存储改为双精度存储

double f64 = 100000000.1;

第一步:将(100000000)10转换为双精度的二进制

整数部分(100000000)10,转换为二进制:
(100000000)10 = (0101111101011110000100000000)2
小数部分:(0.1)10转换二进制
(0.1)10=(0.00011001100110011001100110011)2 总的29位,后面还有无穷0011循环
(100000000.1)10=(-1)^0*(1.0111110101111000010000000000011001100110011001100110011) * 2^26,绿色:26位,红色26位,double的M可以存储52bit

 第二步:从第一步可知

S=0,e=26,E=e+1023=(10000011001)2,
M=0111110101111000010000000000011001100110011001100110

第三步:由第一步和第二步可知,浮点数(100000000.1)10在内存中双精度存储的二进制为:

(0 10000011001 01111101 01111000 01000000 00 00011001 10011001 10011001 10)2

C语言验证:

int main(int argc, char *argv[])
{
        double f64 = 100000000.1;
        printf("f64=%lf\n", f64);
        print_binary(&f64, sizeof(f64));
        return 0;
}

结果:

f64=100000000.100000
Hex: 41 97 d7 84 00 66 66 66
binary:010000011001 01111101 01111000 01000000 00 00011001 10011001 10011001 10

验证和理论分析一致:

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/258343
推荐阅读
相关标签
  

闽ICP备14008679号