浮点数的表示和运算

本文

主要介绍计算机中浮点的表示和运算方法。

版本 说明
0.1 初版发布

背景

处理器有一个很重要的运算单元就是浮点运算,也是处理器性能很关键的一个指标。在对处理器浮点运算单元进行设计和验证之前,必须掌握浮点的表示和运算方法。

重要概念

  • 规格化: 为了保证浮点数的精度,需要在计算过程与计算结果让浮点数保持规格化。判断规格化数的方法是检测阶码是否全为0。如果不是规格化数,就要通过增加阶码右移尾数或者减少阶码左移尾数的办法使其变成规格化数。这个过程叫做规格化。

  • 扩充位: 在运算过程中为了计算方便,会对阶码以及尾数进行扩充。增加了扩充位后,可以判断并防止溢出,并且能显示出隐藏的前导位,还能有助于近似判断。

  • 浮点数的溢出: 上溢: 当浮点数阶码超过了最大阶码时,此时不再运算,进行中断处理; 下溢: 当浮点数小于最小阶码,此时溢出的值很小,处理方式是把尾数置为零,处理器继续运行。

  • 浮点数近似模型: 由于计算机计算的结果精度不可能是无穷大的,所以需要采用某种近似模型。根据IEEE-754标准有4种近似模型。分别是: 靠近最近的偶数、靠近 0( 将超出范围的数据直接切除) 、靠近无穷大方向、靠近无穷小方向。

浮点表示方法

浮点数由三部分组成:符号位、阶码、尾数。

参数 半精度 单精度 双精度
浮点数字长 16bit 32bit 64bit
尾数长度M 10bit 23bit 52bit
符号位S 1bit 1bit 1bit
指数长度 5bit 8bit 11bit
最大指数 +15 +127 +1023
最小指数 -14 -126 -1022
指数偏移量bias +15 +127 +1023

浮点数的表示方法是 x=(-1)^s * (1.M) * 2^(E-bias)。

S是符号位,M是尾数,E是阶码,当阶码E全为0或全为1时,表示该浮点数不是规格化数,可以表示为无穷大、NaN(非数值)。

IEEE规定根据指数和尾数可以表示如下几种特殊值:

  • 零值:阶码全部为0,尾数全部为0,表示0.0,并且+0.0==-0.0
  • 非规格化值:阶码全部为0,尾数非0,表示非规格化值,也就是(-1)^s * (0.m) * 2^(-bias)
  • 规格化值:阶码非0非1,尾数任意,表示规格化值,也就是(-1)^s * (1.m) * 2^(E-bias)
  • 无穷值:阶码全部为1,尾数全部为0,则根据符号位分别表示正无穷大和负无穷大
  • NaN:阶码全部为1,尾数非0,则表示这个值不是一个真正的值(Not A Number)。NAN又分成两类:QNAN(Quiet NAN)和SNAN(Singaling NAN)。QNAN与SNAN的不同之处在于,QNAN的尾数部分最高位定义为1,SNAN最高位定义为0;QNAN一般表示未定义的算术运算结果,最常见的莫过于除0运算;SNAN一般被用于标记未初始化的值,以此来捕获异常。

浮点运算

对阶

所谓对阶是指将两个进行运算的浮点数的阶码对齐的操作。对阶的目的是为使两个浮点数的尾数能够进行加减运算。几点需要注意:

  • 对阶的原则是小阶对大阶,之所以这样做是因为若大阶对小阶,则尾数的数值部分的高位需移出,而小阶对大阶移出的是尾数的数值部分的低位,这样损失的精度更小。
  • 若阶差=0,说明两浮点数的阶码已经相同,无需再做对阶操作了。
  • 采用补码表示的尾数右移时,符号位保持不变。
  • 由于尾数右移时是将最低位移出,会损失一定的精度,为减少误差,可先保留若干移出的位,供以后舍入处理用。

尾数运算

尾数运算就是进行完成对阶后的尾数相加减。这里采用的就是纯小数的定点数加减运算。

结果规格化

为保证浮点数表示的唯一性,浮点数在机器中都是以规格化形式存储的。对于IEEE754标准的浮点数来说,就是尾数必须是1.M的形式。由于在进行上述两个定点小数的尾数相加减运算后,尾数有可能是非规格化形式,为此必须进行规格化操作。

规格化操作包括左规和右规两种情况。

  • 左规操作:将尾数左移,同时阶码减值,直至尾数成为1.M的形式。
  • 右规操作:将尾数右移1位,同时阶码增1,便成为规格化的形式了。

舍入处理

浮点运算在对阶或右规时,尾数需要右移,被右移出去的位会被丢掉,从而造成运算结果精度的损失。为了减少这种精度损失,可以将一定位数的移出位先保留起来,称为保护位,在规格化后用于舍入处理。

IEEE754标准列出了四种可选的舍入处理方法:

  • 就近舍入:这是标准列出的默认舍入方式,其含义相当于我们日常所说的“四舍五入”。
  • 向正无穷舍入:对正数来说,只要多余位不为全0,则向尾数最低有效位进1;对负数来说,则是简单地舍去。
  • 向负无穷舍入:对正数来说,只是简单地舍去;对负数来说,只要多余位不为全0,则向尾数最低有效位进1。
  • 向0舍入:即简单地截断舍去,而不管多余位是什么值。这种方法实现简单,但容易形成累积误差,且舍入处理后的值总是向下偏差。

溢出判断

与定点数运算不同的是,浮点数的溢出是以其运算结果的阶码的值是否产生溢出来判断的。若阶码的值超过了阶码所能表示的最大正数,则为上溢,进一步,若此时浮点数为正数,则为正上溢,若浮点数为负数,则为负上溢;若阶码的值超过了阶码所能表示的最小负数,则为下溢,进一步,若此时浮点数为正数,则为正下溢,若浮点数为负数,则为负下溢。正下溢和负下溢都作为0处理。

浮点舍入

就近舍入

就近舍入可以理解为常说的四舍五入,但是有一点需要注意,浮点中是向偶数舍入的,下面就来解释为什么如此?

传统的四舍五入对于0.5这种中间值并不合理。比如0.5,1.5,2.5,3.5都向上入是1,2,3,4,平均值2.5,比原数均值2高了0.5;向下舍同理。如果是向偶数舍入,则变成0,2,2,4,均值刚好与原均值同。很多带0.5的数,不能全都向上入或全都向下舍,这样会导致整体误差走向极端。如果有一部分向上入,有一部分向下舍,可消除部分统计误差。


文章原创,可能存在部分错误,欢迎指正,联系邮箱 cao_arvin@163.com。