APP下载

二进制浮点数转十进制的快速方法

2021-06-24熊帮玲吴海燕程洋洋

关键词:尾数共用二进制

熊帮玲,吴海燕,程洋洋

(安徽三联学院 电子电气工程学院,安徽 合肥 230601)

计算机中的所有信息,包括图片、视频、音频、文字、数据,全部都以二进制的形式存在[1]。把现实中具体的事物通过建模、抽象等方法用计算机语言表示出来,然后由CPU 进行处理运算,并把结果以一种人类语言反馈给使用者。这个过程中,二进制发挥着难以替代的作用。而日常生活中习惯使用十进制,因此掌握二进制与十进制的转换就显得比较重要[2]。

针对数制的转换,整数形式的支持目前已比较完善,资料也很丰富,如二进制和十进制的相互转换[3]。利用计算器也可以方便地实现各种进制之间的转换。但当前情况下,对内存中浮点数二进制的转换支持并不完善。基于SIMD 的向量浮点单元,可以极大提高运算能力,但同时也增大了验证的难度。特别是在CPU 硅后验证阶段,硬件不可靠的情况下,调试时就需要把内存中浮点数取出然后转换为十进制数去分析是否符合预期。例如在龙芯2K1000[4]平台上进行向量优化时就遇到大量的浮点异常,这与传递到浮点寄存器中的数据大小有关。关于浮点数二进制转换为十进制,目前的多数研究还停留在根据IEEE754 标准去解析计算的阶段,这个计算过程是比较复杂且效率低下的。肖红德[5]对IEEE754 标准进行详细的研究分析和计算,张爱良[6]在标准基础上做出一些优化改进。但这些研究都离不开标准最终仍旧需要繁琐的计算过程,费时费力,计算过程中容易导致错误,实际应用不好普及。

针对以上问题,本文另辟蹊径,抛却直接或间接利用IEEE754标准去计算二进制浮点数对应的十进制数据的传统方法。根据共用体成员变量共用内存地址空间的特性,设计一个共用体,并提供一种算法步骤能够把浮点数的二进制数据转换为十进制。实验表明,本文提出的方法能简单高效地完成浮点二进制数据向十进制转换的问题[7]。

1 IEEE754 浮点二进制算术标准分析

浮点处理的是数的近似表示,通常浮点数的表示都遵守IEEE754 标准[Institute of Electrical and Electronics Engineers 754]。该标准精确定义了一类基本操作应当产生的结果,保证程序员不管用什么机器,只要是同样的输入就能获得同样的结果。当数据在整数和浮点寄存器之间传送时,不做任何数据转换,也不会发生任何异常[8]。IEEE754 标准在编程中并不需要多关注,因为如果在代码中显示给予一个浮点常数,存储时会由计算机自动按照IEEE754 标准格式存储。

在32 位机器上,根据IEEE754 标准,单精度浮点数(32 位)中每个bit 定义如下:

IEEE754 标准规定,单精度浮点数据的存储是按指数和尾数的二进制形式存储的,用二进制的科学计数法来表示。把浮点数对应的内存划分为指数域、尾数域和符号位。指数域没有采用有符号的二进制数形式存储,而是采用偏置方式(指数+127,对于32 位的IEEE 格式,指数域为8 位的长度,可以容纳从0 到255 的值),保证指数域永远为正,这样表示的范围就大一点;尾数介于1 和2 之间,由于尾数的最高有效位永远为1,因此就没必要存储,所以尾数域存储的值是从次高位开始存储的,这样就可以免费获得一个额外精度位的技巧。这种规格化形式对于计算机表示很有用,因为不需要额外的信息记录小数点的位置。图1 所示为浮点数8.25 在内存中的表示。

图1 浮点数8.25 的内存表示

2 二进制转化为十进制理论算法

2.1 整数形式二进制转换为十进制通用算法

在计算机内存系统中,整数是按照固定的算法转化为二进制数进行存储的,这组二进制数每个bit都有固定的权值。二进制数转换成十进制数的基本做法是,把二进制数首先写成加权系数展开式,然后按十进制加法规则求和。比如对于一个8bit 的二进制数据:

转化为十进制数需要按公式(2)所示算法进行计算:

2.2 浮点数二进制转换为十进制通用做法

目前,为了获取浮点数的十进制数据,需要技术人员根据IEEE754标准规定的浮点数的二进制科学计数法表示格式,解析计算得到浮点数的十进制数据。例如,对于单精度浮点数,按照公式(3)计算得到浮点数的十进制数据。sign 是符号位,决定对应的十进制浮点数的正负,0 代表正,1 代表负;mant 是浮点数二进制表示的尾数域,组成浮点二进制科学计数法的小数部分,注意由于二进制的科学计数法的第一位都是1,在表示浮点数时都会省略,所以此处需要再加上整数部分的1,二者拼接成1.xxxx 的形式;bexp 是指数域的值,需要转换为十进制数然后减去127;v是转换后的十进制数据。转换为十进制需要在二进制科学计数的基础上分别把二进制浮点数的整数部分和小数部分分别转换为十进制[9]。

这种获取浮点数十进制数据的方法,需要由技术人员手动或者通过编程,先从浮点数的二进制数据中分别提取符号位、指数域和尾数域的值,然后完成上述较复杂的计算过程,非常容易出错,耗时很长,效率很低。

3 共用体实现二进制向十进制转换 方法

由上文介绍的浮点数二进制转换为十进制的通用做法可以看出传统转换方式存在的弊端,不易在实际中使用[10]。因此,寻找新的转换方法迫在眉睫。事实上,可以在调试过程中使用一些技巧,轻松地获取内存中的二进制浮点数据。针对浮点数特殊的存储形式,通过将得到的浮点数二进制数据赋值给共用体变量的整型成员变量,由于共用体变量中的每个成员变量共用内存地址空间,在将该浮点数的二进制数据赋值给共用体变量的整型成员变量后,也就是将浮点数的二进制数据存储到了该共用体变量的浮点型成员变量所指向的内存地址空间;通过打印共用体变量的浮点型成员变量,即可得到浮点数的十进制数据。该共用体的C++实现形式如下。

该共用体包含2 个成员项,一个是float 类型,另一个是int 类型。选择int 类型是因为在一般情况下它占据的内存空间同float 类型一样,具体可以根据自己使用的CPU 架构确定。这样当一个浮点数传递进来时不至于损失数据,同时也能节省内存空间;其次利用了共用体的成员变量具有共用内存地址空间的特性,这样当把4 个字节的数据传递进来时,2 个成员变量都获得了赋值。对于内存中同一个地址上的二进制数,当做整型还是浮点型是有不同的意义的,虽然其在二进制形式上是一模一样的。也就是说,4 个字节的二进制数据本身是无意义的,当把它看成int 类型的变量时,就具有int 类型的值;当把它看成float 类型的变量时,就按照IEEE754 标准具有float 类型的值。显然这组二进制数据对应的2 种类型的十进制数是有天壤之别的。

C++代码如下。

C++代码中首先定义一个共用体变量ff,把要转换的二进制浮点数据赋值给int 类型的变量,这样float 成员变量也获得了这份二进制数据。再通过GDB(在调试时使用gdb 命令‘p ff.ff’)或printf 函数打印ff.ff,即可获得该二进制数据对应的十进制浮点数大小,以上代码是以printf 函数为例进行操作的。该方法完整过程如图2 所示。

图2 浮点二进制转十进制过程

这个过程也可以反过来,实现的效果就是能方便地把一个浮点数转化为一个IEEE754标准下的二进制数。在将浮点数的十进制数据赋值给共同体变量的浮点型成员变量之后,通过调试工具或者printf函数,打印共同体变量的整数型成员变量,即可打印出浮点数的二进制数据。由于高级语言能够独立于不同处理器的特性,使用编译器为不同机器生成不同的目标代码(或机器指令)。因此,经过编译器的转换,从高级语言出发设计的共用体在不同架构的微处理器上仅表现为不同的指令码,本文提出的方法在不同架构的微处理器上都可适用。

4 实验与应用分析

在龙芯2K1000 平台上进行skia 图形库浮点向量优化时,遇到大量float exception 导致的程序崩溃问题[11]。经过仔细分析代码流程与反汇编指令序列,确定代码无误的情况下查阅2k1000 GS264 处理器核用户手册,最终确定float exception 是向量浮点指令传入的操作数小于手册上规定的最小规格化数导致的。使用GDB 调试工具可以获取到指令与寄存器中的二进制数据,但是对于这些二进制对应的十进制数是否都是小于最小规格化数是不好确定的。对于优化过程中使用的大量浮点指令传入的操作数,以及程序运行时的每一次加载进浮点向量寄存器的二进制数据,显然是不能通过传统方式计算的。通过使用本方法,能够方便快速地求出发生浮点异常时寄存器中二进制数据对应的十进制数据,进而和手册进行比较,最终确定了所有的浮点异常都是这个原因导致的。表1 是利用本文提供的方法进行二进制和十进制的数据转换结果。

表1 浮点二进制数转化十进制

5 结论

本文通过设计一个共用体类型,提出将浮点数的二进制数据赋值给共用体变量的整型成员变量,借助GDB 或者printf函数去获取对应的十进制值的方法,解决了传统方式浮点二进制数据向十进制数据转换过程复杂及容易出错的问题。在需要大量进行这种数据转换的场合,本文提出的方法更是有很高的效率。本文仅通过float 类型的数据作为例子介绍浮点二进制向十进制转换的方法,对于double 类型,以及浮点向量类型的数据转换,也具有抛砖引玉的意义。

猜你喜欢

尾数共用二进制
有用的二进制
用Scratch把十进制转为二进制
有趣的进度
2019年度上半年《启迪与智慧》上下半月刊、《幽默与笑话》上下半月刊、《拳击与格斗》上半月刊抽大奖中奖结果
2019年度下半年《启迪与智慧》上下半月刊、《幽默与笑话》上下半月刊、《拳击与格斗》上半月刊抽大奖中奖结果
多种方法解“妇人洗碗问题”
活该你单身
有趣的九九乘法表
还有一行
同时进行