对程序员来说,用来表示小数的浮点类型是最常接触到的基础数据类型之一。它包括单精浮点数(float,4字节)和双精浮点数(double,8字节)两种。但为何称表示小数的数据类型为浮点类型呢?顾名思义,所谓浮点即指小数中的小数点是可浮动的,但它是如何浮动的呢?能不能不浮动?

  其实,计算机中表示小数的数据类型可以分为定点数和浮点数两种。只是因为定点数表示小数时不够灵活,故逐渐为浮点数所替代。

定点数

  以32位机为例,所谓定点数即指在32位的存储结构中,表示整数的数值部分,与表示小数的数值部分各占固定的位数,如图1:

stm32f103怎么设置支持浮点运算_宏定义中有浮点数

(图1)

最高位31位为符号位,0表示正,1表示负;23~30的8位表示整数部分,可表示的数值范围为[0,255];0~22位为小数部分。小数点相当于固定在了22位与23位之间。小数点的位置可根据需要进行调整,通常定点数会被表示为纯小数或纯整数两种。当小数点固定在数值部分的最高位之前时,即在31位与30位之间时,表示纯小数;而当小数点固定在数值部分的最后面,即在0位右侧时,表示纯整数。

浮点数

        IEEE标准规定,浮点数由符号域(+/-)、指数域(E)和尾数域(M)三部分构成,其数值表达式为:D = (+/-) M * 2^E,其中,D为浮点数转换后所得的小数,M为尾数, E为指数。IEEE 754中定义了单精度与双精度两种浮点格式,下面以单精度浮点格式为例,如图2:

stm32f103怎么设置支持浮点运算_两字节表示浮点数_02

(图2)

  最高位,31位为符号位,0表示正,1表示负。

         23~30位为指数位,其中约定该部分的值与127(2^7-1)的差为实际的指数值。即,当该值为127时,其与127的差值为0,表示小数点不移位;而当该值为126时,其与127的差为-1,表示小数点向左移一位;而当该值为128时,其与127的差为1,表示小数点向右移一位。如图2例中,指数部分值为127,与127的差值为0,故不需要移动小数点。

        0~22为尾数部分,即数值的真实部分。这里需要注意的是,尾数部分虽然只是23位,但实际表示的数值为24位。被隐藏的一位,其值永远为1,在小数点左侧,并不被持久化保存。如图2中,尾数部分补足隐藏位后的实际二进制格式为1.10000000000000000000000,因为不需要移动小数点,故转化为十进制后值为1.5。再如图3:

stm32f103怎么设置支持浮点运算_stm32f103怎么设置支持浮点运算_03

(图3)

尾数为全0,但补足隐藏位后的实际值应为1.00000000000000000000000,由于指数部分为126,则小数点需要向左移动1位,故变为0.100000000000000000000000,转换为十进制即为0.5。

       下面再看看十进制小数是如何转变为浮点格式的,如小数2.6,其整数部分2转换为二进制表达为10;小数部分0.6 = 0.5 + 0.0625 + 0.007825 + … = 2^-1 + 2^-4 + 2^-5 + … 即转换为二进制格式为0.10011001100110011001101。合并整数与小数两部分,其二进制格式为

10. 10011001100110011001101。我们需要让小数点前面只有一个1,那么我们需要将小数点向左移动1位,并将该值与指数基数127相加,最终转换所得的浮点数格式如图4:

stm32f103怎么设置支持浮点运算_宏定义中有浮点数_04

(图4)

由这次转换我们也可以看出,0.6的尾数表达并非是一个精准表达,而是一个近似值,尾数部分呈现1001的循环,也是基于此,我们在做浮点数计算时,有时会出现和我们预期稍有差别的结果。如计算2.6 / 2,按我们理解应该得1.3,可实际运算后得到的结果为1.2999995,一个非常接近我们认知结果的数值。

       最后,由于浮点数的转换与计算相对而言比较复杂,故计算机中有了专门用于加速处理浮点数计算的计算单元FPU(Float Point Unit)。