IEEE 754 浮点算数的存储

         在计算机中,对于整数和浮点数都是按照二进制的方式进行存储的。如果是整数,会直接将传入的数字存储在通用寄存器中,那么对于浮点数的存储呢?32位的浮点存储是什么样的,64位的存储是什么样的?128位的数据存储又是怎么样的呢?本节主要在LA架构上进行实验,其他结构的分析类似。如果有疑问的地方,还请指出提问。

一、基本概念

IEEE 754 标准形式(科学计数法):

                        V=(-1)^{s} x M x 2^{E}

其中,V表示真正存储的数值,对数值进行科学计数法表示。

(1)符号:s决定数的正负

(2)尾数:M是一个二进制的小数

(3)阶码:E的作用是对浮点数加权,权重为2的E次幂

        任何一个数字在计算机中存储都是按照(符号、阶码、阶码)的形式表示的。

二、示例分析

        此次所有的示例分析都是基于LA架构上,机器属于小端存储。

1.整数(long int)

整数的存储分为高位优先存储(big-endian)和低位优先存储(little-endian)。

高位优先存储:高位首先存在低地址。 
低位优先存储:低位首先存在低地址。

编写测试用例,调试结果如下

5         long int  a=1,b=2;

=> 0x0000000120000680 <+16>:addi.w $r12,$r0,1(0x1)  //存储数字1

   0x0000000120000684 <+20>: st.d $r12,$r22,-24(0xfe8)

   0x0000000120000688 <+24>: addi.w $r12,$r0,2(0x2)   //存储数字2

   0x000000012000068c <+28>: st.d $r12,$r22,-32(0xfe0)

...

     可以看出定义的a,b,可以存储在通用寄存器中,然后保存到内存地址上,存储的值很容易观察。

2.单精度(32位)

其中:s(1位) 、exp(k=8) 、frac(n=23位)

举例说明:

数字:112.5= 112 +0.5 (以小数点为分界线)

转换为二进制表示==》 1110000.1 ——》科学计数 1.1100001 x 2^{6}

其中,数字6表示阶码,  1100001 表示尾数

注意:在寄存器中存储浮点数时,对于不同的精度的数,需要添加对应的偏移量。三种类型的精度偏移量如下所示:

单精度偏移量:127=2^{8-1} -1

双精度偏移量:1023=2^{11-1}-1

四精度偏移量:16383=2^{15-1}-1

        其中数字8,11,15分别是32,64和128浮点标准规定的阶码位数(固定)。所以在存储时对于单精度的浮点数112.5的阶码为 6+127=133

其32位的存储形式如下所示:

0100 0010 1110 0001 0000 0000 0000 0000 ==》表示为0x42e1 0000

验证分析的准确性:

测试用例:

//test.c

#include<stdio.h>

int main()

{

        float a=112.5,b=2.5;

        printf("a=%f,b=%f\n",a,b);

        return 0;

}

命令:gcc test.c -g -o test

gdb 调试:gdb test 

设置断点,运行

查看汇编。

        float  a=112.5,b=2.5;   

=> 0x0000000120000680 <+16>: pcaddu12i $r12,0

   0x0000000120000684 <+20>: addi.d $r12,$r12,260(0x104)

   0x0000000120000688 <+24>: fld.s $f0,$r12,0

   0x000000012000068c <+28>: fst.s $f0,$r22,-20(0xfec)

   0x0000000120000690 <+32>: pcaddu12i $r12,0

   0x0000000120000694 <+36>: addi.d $r12,$r12,248(0xf8)

   0x0000000120000698 <+40>: fld.s $f0,$r12,0

   0x000000012000069c <+44>: fst.s $f0,$r22,-24(0xfe8)

调试结果:

(gdb) p $f0

$1 = {f = 112.5, d = 8.000001993146725}

//其中,输出的浮点寄存器f0的值包括两部分,32位表示的浮点值和64位标识的浮点值。(浮点寄存器f0是64位的)

3.双精度(64位)

其中,符号位s:1位 ,阶码exp:k=11位 ,尾数frac:n=52位。

编写测试用例,gdb调试观察,如单精度的类似。

        double  a=1.5,b=2.5;

   0x0000000120000680 <+16>: pcaddu12i $r12,0

   0x0000000120000684 <+20>: addi.d $r12,$r12,248(0xf8)

   0x0000000120000688 <+24>: fld.d $f0,$r12,0

=> 0x000000012000068c <+28>: fst.d $f0,$r22,-24(0xfe8)

   0x0000000120000690 <+32>: pcaddu12i $r12,0

   0x0000000120000694 <+36>: addi.d $r12,$r12,240(0xf0)

   0x0000000120000698 <+40>: fld.d $f0,$r12,0

   0x000000012000069c <+44>: fst.d $f0,$r22,-32(0xfe0)

调试的结果:

(gdb) p $f0

$1 = {f = 0, d = 1.5} //分别显示的是单精度和双精度的数字

4.四精度(128位)

对于128位的浮点存储,在LA架构上是通过两个通用的64位寄存器实现的。

127

126          112

111                  64

63                 0

符号(1)

阶码(15)

尾数(高:48)

尾数(低:64)

测试用例:

#include<stdio.h>

int main()

{

        __float128 a=1.5,b=2.5; //__float128类型有可能一些编译器不支持,也可以直接使用_Float128 进行替换

        printf("a=%Lf,b=%Lf\n",a,b);

}

编译命令:gcc test_float128.c -g -o test_float128

        执行:./test_float128

  gdb调试:gdb test_float128      

调试过程如下:

        __float128  a=1.5,b=2.5;

=> 0x0000000120000650 <+16>: pcaddu12i $r12,0

   0x0000000120000654 <+20>: addi.d $r12,$r12,288(0x120)

   0x0000000120000658 <+24>: ld.d $r13,$r12,8(0x8) //加载a=1.5 的高64位到r13寄存器中

   0x000000012000065c <+28>: ld.d $r12,$r12,0 //加载a=1.5的低64位到r12寄存器中

   0x0000000120000660 <+32>: st.d $r12,$r22,-32(0xfe0)

   0x0000000120000664 <+36>: st.d $r13,$r22,-24(0xfe8)

   0x0000000120000668 <+40>: pcaddu12i $r12,0

   0x000000012000066c <+44>: addi.d $r12,$r12,280(0x118)

   0x0000000120000670 <+48>: ld.d $r13,$r12,8(0x8)  //对变量b进行处理

   0x0000000120000674 <+52>: ld.d $r12,$r12,0

   0x0000000120000678 <+56>: st.d $r12,$r22,-48(0xfd0)

   0x000000012000067c <+60>: st.d $r13,$r22,-40(0xfd8)

调试结果:

(gdb) p $r12

$1 = 0                        //低64位的结果

(gdb) p $r13

$2 = 4611545280939032576

(gdb) p /x $r13

$3 = 0x3fff800000000000   //高64位的结果

(gdb)

        如上所示,a=1.5存储在寄存器中的表示为 0x3fff 8000 0000 0000 0000 0000 0000 0000

(1)从寄存器中的值,求真实浮点数值

3fff==>0011 1111 1111 1111

其中,符号位为0,表示正数;

011 1111 1111 1111表示15位阶码(E);

M=8000 0000 0000 0000 0000 0000 0000 表示尾数(112位)

v(b)=1.1x2^{n}(次数的n=16383-(2^{15-1}-1)=0,1的数值是通过尾数决定的,科学计数法第一个数一定是1)

   可以得到十进制数是==>v=2^{0}+2^{-1}=1.5

(2)从浮点数求存储在寄存器中的值

        1.5 =》1.1 x 2^{0}

符号:0

存储的阶码=0+2^{15-1}-1=16383 =0x3fff

尾数 1000 0000000 ...(尾数从科学计数法的小数点后写起,不够位数的全补0,共需要补充111个0)

        此时,就可以可以将1.5和存储的 0x3fff 8000 0000 0000 0000 0000 0000 0000数值相互联系和互相转化了,使得在调试过程中对于浮点数的存储的原理更加清楚。

        为何总结此浮点数的内容呢?

       (1)在实现编译器GCC中以__float128类型的builtin函数时,发现需要传入的是128位的浮点类型变量,和以前在通用寄存器中存储的整数表示方式有所不同,以及他们是如何存储的。

        (2)32,64和128位浮点数的存储与表示,是学习计算机中很重要的事情,需要探索其背后的原理。

三、总结

        常见的不同精度的二进制表示规范如图所示。

        

 四、参考资料

  1. https://blog.csdn.net/u012713968/article/details/50481699(整数)
  2. https://www.cnblogs.com/oasisyang/p/13788555.html(32,64位的浮点数)
  3. https://www.bilibili.com/video/BV1Ct4y1c7Zx/?spm_id_from=pageDriver&vd_source=288ff536a8777b3c33054ab46a4fa348(b站)
  4. https://zhuanlan.zhihu.com/p/480834719(知乎,包含ieee754的手册)

               

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值