计算机中浮点数的表示

2021-09-30  本文已影响0人  和谐共处

浮点数转二进制浮点数

整数部分:

  1. 整数部分不断除以2,保存商和余数。
  2. 判断商是否等于0,如果等于0到第3步,否则回到第1步。
  3. 将每一步得到的余数倒序保存,得到整数部分的二进制表示。

小数部分:

  1. 小数部分不断乘以2,保存结果的整数和小数。
  2. 判断结果的整数是否等于1,如果等于1到第3步,否则回到第1步。
  3. 将每一步结果的所有整数顺序保存,就得到小数的二进制表示

例如:浮点数 4.8125
整数部分:

4/2     = 2     0
2/2     = 1     0
1/2     = 0     1
所以整数部分 4 的二进制为 100

小数部分:

0.8125*2    = 1.625     1
0.625*2     = 1.25      1
0.25*2      = 0.5       0
0.5*2       = 1.0       1
所以小数部分 0.8125 的二进制位 1101

将整数与小数部分连接起来就是 100.1101,即4.8125=100.1101_2

再如:浮点数 0.05
因为只有小数所以只处理小数部分:

0.05*2      = 0.1 0
0.1*2       = 0.2 0
0.2*2       = 0.4 0
0.4*2       = 0.8 0

0.8*2       = 1.6 1
0.6*2       = 1.2 1
0.2*2       = 0.4 0
0.4*2       = 0.8 0

0.8*2       = 1.6 1
0.6*2       = 1.2 1
0.2*2       = 0.4 0
0.4*2       = 0.8 0

0.8*2       = 1.6 1 
......      无限循环


所以0.05的二进制 = 0.0000 1100 1100 1100 1100 1100 1101 .... 

可以看到二进制小数是无法准确表示浮点数的,所以就有了精度一说.
单精度浮点数用32位二进制表示如下:

符号位1位  阶码8位  小数位23位
单精度.jpg

双精度浮点数用64位二进制表示如下:

符号位1位  阶码11位  小数位52位
双精度.png

由上可知,
单精度浮点数精度为 pow(2,23) = 8388608 = 0.8388608 x pow(10,7)
所以单精度浮点数对应的10进制精度为 7 位多
双精度浮点数精度为 pow(2,52)-1 = 4503599627370496 = 0.4503599627370496 x pow(10,16)
所以双精度浮点数对应的10进制精度为 16 位多

浮点数的计算机表示 (IEEE754标准)

移码(又叫增码)是符号位取反的补码,一般用指数的移码减去1来做浮点数的阶码,
引入的目的是便于浮点数运算时的对阶操作。为了保证浮点数的机器零为全0。

对于定点整数,计算机一般采用补码的来存储。
正整数的符号位为 0,反码、补码、原码都一样。
负整数的符号位为 1,原码、反码和补码的表示各不相同,

由原码变成反码和补码有如下规则:

  1. 原码符号位为1不变,整数的每一位二进制数位取反得到反码;
  2. 反码符号位为1不变,反码数值位最低位加1得补码。
比如,以一个字节 8bits 来表示 -3,
那么-3的各种码,原码-->反码-->补码--->移码
原码:[ − 3 ] = 1 000001 1,最高位1位符号位
反码:[ − 3 ] = 1 111110 0,原码符号位不变,其他位取反
补码:[ − 3 ] = 1 111110 1,反码符号为不变,最低位加1
移码:[ − 3 ] = 0 111110 1,补码符号为取反

浮点数二进制表示:
V=(-1)^s*(1.M)*2^E

s:符号位,0正数,1负数
M:小数部分,使用原码表示
E:阶码,使用其原码+127表示或者用其移码-1表示,

比如十进制4.5的单精度浮点数的二进制 = 100.1_2 表示为上述公式则为
4.5=(-1)^0*(1.001)*2^2
看到这里的E2,那么它在计算机实际存储为 2 + 127 = 129 = 10000001_2

根据上面的公式各部分表示的规则得到如下表:

符号位s:1位 阶码E:2~9位 小数位M:10~32位
0 2+127=129 001
0 10000001 00100000000000000000000

符号位为0表示是正数,所以4.5的二进制存储为0 10000001 00100000000000000000000,即1000000100100000000000000000000_2 = 0x40900000

以下tool.c是一个测试工具:

// tool.c
#include <stdio.h>
#include <stdlib.h>
// 打印一个计算机存储的浮点数值
void print_bin_to_float(unsigned long hex){
    float* fp=(float*)&hex;
    printf("%lx,%f\n",hex,*fp);
}
// 打印一个浮点数在计算机存储的十六进制表示
void print_float_to_bin(float f){
    unsigned long* pf = (unsigned long*)&f;;
    printf("%f,0x%lx\n",f,*pf);

}
// 反转字符串
char * reverse_str(char *str, long len){
    char    *start = str;
    char    *end = str + len - 1;
    char    ch;

    if (str != NULL)
    {
        while (start < end)
        {
            ch = *start;
            *start++ = *end;
            *end-- = ch;
        }
    }
    return str;
}
// 十六进制转二进制字符串
char * hex_to_bin(unsigned long long hex,int bin_max_len){
    printf("0x%llx,hex_to_bin:\n",hex);
    char bin_ar[1024] = {0};
    long count = 0;
    unsigned long long quotient = 0;
    int remainder = 0;
    do{
        quotient = hex / 2;   
        remainder = hex % 2;
        //转换成显示的数字字符 
        char save = '0' + remainder;
        bin_ar[count] = save;

        hex = quotient;
        count ++;

    } while (quotient != 0);
    while (count < bin_max_len){ 
        bin_ar[count] = '0';
        count ++;
    }
    char *result = reverse_str(bin_ar,count);
    printf("0b%s\n",result);
    printf("\n");
    return result;
}
int main(){
    print_float_to_bin(4.5);        // 0x40900000
    print_float_to_bin(0.5);        // 0x3f000000
    print_float_to_bin(0.05);       // 0x3d4ccccd
    print_float_to_bin(0.15625);    // 0x3e200000

    /*
    4.5
    0x40900000,hex_to_bin:
    0b01000000100100000000000000000000
    */
    hex_to_bin(0x40900000,32);

    return 0;
}



下面是我们把上述二进制转为二进制浮点数

4.5
0x40900000 十六进制表示
sign    exponent    fraction
0       10000001    00100000000000000000000
0       129         00100000000000000000000
阶码 129-127 = 2
0       2           00100000000000000000000
先向右移动小数点2位
0       2           00.100000000000000000000
在向首位补1
0       2         1 00.100000000000000000000
结果 100.100000000000000000000

如果得到阶码是负数比如下面的0.50.05,规则是先在首位补1,然后向左移动小数点,不够补0

0.5
0x3f000000 十六进制表示
sign    exponent    fraction
0       01111110    00000000000000000000000
0       01111110    00000000000000000000000
0       126         00000000000000000000000
阶码 126-127 = -1
0       -1          00000000000000000000000
先在左侧补1
0       -1        1 00000000000000000000000
再向左移动小点1位
0       -1      0.1 00000000000000000000000
结果就是 0.1 00000000000000000000000

0.05
0x3d4ccccd 十六进制表示
sign    exponent    fraction
0       01111010    10011001100110011001101
0       122         10011001100110011001101
阶码 122-127 = -5
0       -5          10011001100110011001101
先在左侧补1
0       -5        1 10011001100110011001101
再向左移动小点5位,不够插入0
0   -1      0.00001 10011001100110011001101

结果就是 0.0000110011001100110011001101

综上我们知道
阶码为正小数点右移,先移动小数点再补1
阶码为负小数点左移,先补1再移动小数点,不足补0

二进制浮点数转10进制浮点数,这里是单精度,双精度同理

0.15625
0x3e200000
sign    exponent    fraction
0       01111100    01000000000000000000000
0       01111100    01000000000000000000000
0       124         01000000000000000000000
0       -3          01000000000000000000000
先在左侧补1
0       -3          1 01000000000000000000000
再向左移动小点3位,不够插入0
0       -3          0.001 01000000000000000000000
结果就是 0.00101000000000000000000000 = 0.00101



根据公式定义:
V=(-1)^s*1.M*2^E

0.15625二进制表示如下:
V=(-1)^0*1.01*2^{-3}

转换如下:
sign=+1
exponent = (-127) + 124 = -3
fraction = 1*2^0 + 0*2^{-1} + 1 * 2^{-2} = 1.25
value= (+1) * 1.25 * 2^{-3} = +0.15625

参考

IEEE754 Wiki
单精度浮点数
双精度浮点数
浮点数表示
二进制浮点数在线转换

上一篇下一篇

猜你喜欢

热点阅读