Go语言底层原理剖析
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

2.3 IEEE-754浮点数标准

IEEE-754规范使用以2为底数的指数表示小数,这和使用以10为底数的指数表示法(即科学计数法)非常类似。表2-1给出了几个例子,如0.085可以用指数的形式表示为1.36×2^-4,其中1.36为系数,2为底数,-4为指数。

表2-1 数字的表示方法示例

IEEE-754的浮点数存在多种精度。很显然,更多的存储位数可以表达更大的数或更高的精度。在高级语言中一般存在两种精度的浮点数,即大部分硬件浮点数单元支持的32位的单精度浮点数与64位的双精度浮点数。如表2-2所示,两种精度的浮点数具有不同的格式。

表2-2 单精度与双精度浮点数格式

其中,最开头的1位为符号位,1代表负数,0代表正数。符号位之后为指数位,单精度为8位,双精度为11位。指数位存储了指数加上偏移量的值,偏移量是为了表达负数而设计的。例如当指数为-4时,实际存储的值为-4+127=123。剩下的是小数位,小数位存储系数中小数位的准确值或最接近的值,是0到1之间的数。小数位占用的位数最多,直接决定了精度的大小。以数字0.085为例,单精度下的浮点数表示如表2-3所示。

表2-3 数字0.085的单精度浮点数表示

2.3.1 小数部分计算

小数部分的计算是最复杂的,其存储的可能是系数的近似值而不是准确值。小数位的每一位代表的都是2的幂,并且指数依次减少1。以0.085的浮点表示法中系数的小数部分(0.36)为例,对应的二进制数为010 1110 0001 0100 0111 1011,其计算步骤如表2-4所示,存储的数值接近0.36。

表2-4 小数部分计算步骤

续表

那么小数位又是如何计算出来的呢?以数字0.085为例,可以使用“乘2取整法”将该十进制小数转化为二进制小数,即

0.085(十进制)

=0.00010101110000101000111101011100001010001111010111000011(二进制)

=1.0101110000101000111101011100001010001111010111000011×2-4

由于小数位只有23位,因此四舍五入后为010 1110 0001 0100 0111 1011,这就是最终浮点数的小数部分。

2.3.2 显示浮点数格式

Go语言标准库的math包提供了许多有用的计算函数,其中,Float32可以以字符串的形式打印出单精度浮点数的二进制值。下例中的Go代码可以输出0.085的浮点数表示中的符号位、指数位与小数位。

输出结果为:

为了验证之前理论的正确性,可以根据二进制值反向推导出其所表示的原始十进制值0.085。思路是将符号位、指数位、小数位分别提取出来,将小数部分中每个为1的bit位都转化为对应的十进制小数,并求和。

符号位、指数位、小数位,以及最终结果输出如下,验证了之前的理论。