一直对于计算机的浮点数操作原理以及指令很模糊,特别是关于浮点数操作的汇编指令以及寄存器方面的知识,这里记录下(所使用的标准是IEEE754)

关于浮点数在内存中的存储

下面的图是32位浮点数与64位浮点数在内存中的存储形式,和计算公式

如果exp全为1就代表无限大或者NaN(not a number),如果exp全为0代表非规格化

随后在引入一个概念Bias,计算公式为:Bias = 2k-1-1,其中k等于exp所占的二进制位数(32位的浮点数占8位,64位的浮点数占11位)32位的Bisa=127,64位的Bisa=1023

计算公式

浮点数在内存中的存储分为三段:

  1. 符号位S
  2. 阶码E
  3. 尾数M

理清E,exp,Biased三者的关系

三者的计算公式为E = exp - Biased(特别的,如果exp等于0,则公式变为E = 1 - Biased

一定要注意阶码E不会直接存在内存中,而是通过上面的计算将阶码E转化为exp存放在内存中

举两个例子

将数12345转化为浮点数的16进制形式(用32位为例子)

  • 首先将12345转化为二进制数形式

    1
    12345 = (11000000111001)2

    得到12345 = (1.1000000111001)2 x 213,那么现在尾数就是1.1000000111001,将小数点前面的1舍去,后面补0直到长度为23位得到尾数M的二进制表示形式,还有阶码E=13

    1
    10000001110010000000000
  • 计算exp

    计算的数字是32位的,通过上面计算得到阶码E=13,在通过公式exp=E+Biased,得到exp=13+127=140

    1
    140 = (10001100)2
  • 将三者组合

    1
    2
    0	  10001100	10000001110010000000000
    s exp frac
    1
    (01000110010000001110010000000000)2 = 0x4640E400

将1.8转化为浮点数的16进制形式(用32位为例)

  • 将1.8转化为二进制形式

    1
    1.8 = (1.11001100110011001100110)2

    由于1.8转化为二进制是一个无线循环的数字,所以取前23位并向前进一位

    这里阶码E=0

  • 计算exp

    公式exp=E+Biased,得到exp=0+127

    1
    127 = (01111111)2
  • 将三者组合

    1
    (00111111111001100110011001100116)2 = 0x3FE66666

验证

使用gdb调试看看是否是这样存储在内存中,使用命令gcc -g -m32 check.c -o check编译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>

void func1(float a, float b) {
float x = a;
float y = b;
}

int main() {
float a = 12345;
float b = 1.8;
func1(a, b);

return 0;
}
1
2
3
4
5
  0x565561ce <main+34>                  push   dword ptr [ebp - 4]
0x565561d1 <main+37> push dword ptr [ebp - 8]
► 0x565561d4 <main+40> call func1 <func1>
arg[0]: 0x4640e400
arg[1]: 0x3fe66666

可以看到和上面计算的值是一样的,64位的双精度浮点数也是这样计算

浮点数操作常用指令

现在使用的很多浮点数都是双精度浮点数,官方也推荐使用双精度浮点数

  • 移动指令

  • 浮点数转整形指令

  • 整形转浮点型指令

  • 浮点数操作运算