IEEE 754标准和浮点数学习


浮点数的学习契机是我要搞清楚什么是单精度浮点数,什么是双精度浮点数,一个问题,引发了一系列的深入学习。

一.基础知识

在开始正式学习之前,先聊一下计算机的存储方式,我们都知道,电子计算机的原理就是利用通电、断电(或曰高电平低电平)这两个状态来表示布尔代数中的逻辑真和逻辑假,对应的就是1和0,因此我们说计算机的最小单位是bit(比特/位),能够表示0或者1。

形如下面的样子:

那如何表示形如1.134441的数据呢?于是有了一个标准,也就是IEEE754。

在学习这个标准之前,先简单介绍一下科学计数法,因为这个标准是借助于这种记数方式的。

二.科学计数法

我们在表示一个数的时候,如:327.849,可以用科学计数法来表示:

+3.27849 × 10^2

由三部分组成:

+是符号

10^2中的2是指数

3.27849是有效数

指数部分,根据正负,我们可以知道把327.849向左还是向右移动几个小数点。

.名次对照

这部分知识名词有点多,因此写了下面的对照表,看不懂的话可以阅读完全部文章后再对照。

名词 解释
sign/符号位 标识正负的符号位,0正,1负
指数/阶码 这两个概念经常被混淆。在本文中,指数指的是真实的指数值,阶码指的是偏移后的指数值。即:指数 + 偏移量 = 阶码
fraction/尾数 fraction和尾数也容易被混淆。在IEEE754标准中,fraction仅仅存入的是小数点右边的值。除了0之外,IEEEE754中有效数是1.xxx 或者0.xxx,fraction存入的部分仅仅是xxx。因此尾数应该指的是完整的有效数。而fraction是去掉了1.或者0.之后的xxx部分。
指数偏移值 为了让指数存入到计算机中的值全部是正数,因此出现了这个偏移量。指数 + 偏移量 = 阶码
特殊值 正/负无穷,非数值(NaN)

.IEEE754标准

名词 解释
规约形式的浮点数 尾数是1.xxx
非规约形式的浮点数 尾数是0.xx,一般表示极小的浮点数,为了处理突然式下溢出
正负零 阶码是0,指数是-127,尾数是0
特殊值 正/负无穷,非数值(NaN)

.IEEE754标准-规约形式的浮点数

由于浮点数分为规约形式的浮点数、非规约形式的浮点数、零和特殊值。我们先不考虑非规约形式的浮点数和零和特殊值,只考虑规约形式的浮点数,下面讲的是规约形式的单精度浮点数(最常用的)。

在维基百科中,对浮点数的表示方法为:

Value(值) = sign(符号位)× exponent(阶码)×fraction(分数值/小数)

对应下面这张图片。

上面的图是表示的0.15625(十进制)这个小数,在计算机中存储的样子。

然后我们通过解释这张图来学习IEEE754标准。

sign这个就是符号位,0代表正,1代表负。上图等价于下面的式子

+2^{01111100}×1.01000000000000000000000 (因为是2进制,所以是2的n次方而不是10的;二进制的有效数只要不是0,那么肯定是以1开头的数,而且规约形式的浮点是定义为1开头的)

我们知道,如果n是正,那么小数点向右移动n位,如果n为负,那么小数点向左移动n位。

01111100(二进制阶码) = 124(10进制阶码)。但是这个值不能直接带入到2的n次方中使用,因为指数这个值实际上等于指数真正的值加上一个偏移量后,得到的阶码值,来存储的(为什么要加偏移量后面会解释)。

比如说,实际指数是17(10进制),那么存入到磁盘中的值,需要加上偏移量127(10进制),即144(十进制阶码),把这个值转成二进制存入到上面图片的exponent部分。

那么根据这个规则我们可以算出实际的指数是124(阶码)-127(偏移量)=-3(真实的指数)。

fraction要格外注意,图中01000000000000000000000直接转换为10进制为2097152,在移动小数点的时候不能使用十进制,而需要用二进制去移动,因为在IEEE754标准中,针对的是二进制的数。

另外,所有的非0有效数,都可以表示为1.xxx × 2的n方,所以为了节省存储,上面图片的fraction部分是省略了1的,我们整合起来就是:1.01000000000000000000000。

综合上面的三个部分后,我们知道这个算式应该是:

2^{−3} ×1.01000000000000000000000

上面我们提到过,根据指数n来移动小数点,因此上面算式的结果等价于:

0.00101000000000000000000000 = 0.00101。

注意!这是二进制,我们把0.00101转成10进制为:0.15625。这就是开头我们说的结果啦。

知识点:

  1. exponent 称为“指数的偏移后的值”,在中文中,常常称为阶码。
  2. 阶码的取值范围?由于阶码由8比特组成,1比特有0或者1,因此理论上可以表示的数字范围最大是:2^8 = 256个数字。但是实际上第一个最高位是代表符号(正负的),因此去掉后的范围是2^7= -127到128之间,但是-127和128这两个有特殊用处(下面会说明),因此“指数的实际值”的取值范围就是-126-127之间。但这不是阶码的范围,阶码是“指数的实际值”+ “固定的偏移值”,因此阶码的范围是1-254,是的,阶码肯定是大于0的(这种移码表示的指数部分,中文称作阶码)。
  3. 为什么要有“固定的偏移值”,偏移值的大小是怎么来的?很多地方的解释比较难以理解,实际上它的好处就是上一条的结论,就是偏移值的存在导致阶码肯定是大于0的,换句话讲就是我们可以用正数来表示负的指数。这个带来的好处就是计算机在处理减法运算时有局限性,采用移码的方式,可以实现计算机最舒服的数据格式,即:a - b = a + (-b),也就是计算机只会作加法。
  4. 这个“固定的偏移值”是怎么算出来的?IEEE754规定这个值为:

    2^{(e−1)} −1 其中e代表的exponent的位数,单精度浮点数上图可知,就是8

现在来定义一下规约形式的浮点数:

如果浮点数中的阶码范围在 0 < exponent < 2^e -2 之间,即上面说的[1,254]之间(指的是单精度浮点数哦),并且在科学计数法下,小数部分是以1开头的,即1.xxx。那么这个浮点数被称为规约形式的浮点数。

.IEEE754标准-非规约形式的浮点数

如果浮点数的指数部分的阶码是0,小数部分非零,那么这个浮点数将被称为非规约形式的浮点数。一般是某个数字相当接近零时才会使用非规约型式来表示。

非规约形式的浮点数,指数偏移值比规约形式浮点数少1,即126。

因此非规约形式的浮点数的指数真实值是-126(阶码0),和规约形式的浮点数阶码1(指数真实值-126)相等。但是两者的尾数不同,一个0.xxx,一个1.xxx

为何非规约形式的浮点数的偏移值是-126而不是127?没有深入去了解,可能是为了和规约浮点数的数间距保持一致?都是2^{-126},也就是实现渐进式下溢出。

知识点:

  1. 非规约形式的浮点数,它的“固定偏移值”不是127而是126。
  2. 非规约形式的浮点数,一般是用来表示一个数字非常接近于0时才会使用的。
  3. 鉴于上一条,我们有非规约形式的浮点数,一般用于小数是0.xxxx这种情况的小数部分(当然1.xxx也是可以正常使用的,只是我们约定接近于0才用非规约,所以是0.xxx)

. IEEE754标准-特殊值

特殊值指的是正负无穷和非数(NaN)

  1. 如果阶码全是1=255(真实指数是128),并且小数部分是0,这个数是正负无穷(和符号位相关)
  2. 如果阶码全是1=255(真实指数是128),并且小数部分为非0,这个数是非数(NaN)

八.正负零

如果阶码是0(真实的指数是-127)并且尾数的小数部分是0,这个数±0(和符号位相关)

.简单总结

  1. 我们上面第三章讲过,-127和128被用作特殊值处理,-127给了0,128给了正负无穷和NaN。
  2. 我们上面推导的都是单精度浮点数,双精度浮点数的规格可不是这样哦,原理一样,这里不作讲解啦。
  3. 一个浮点数实际上是表示了周围的一个有理数区间,不是精确值,而是代表了一个范围。
  4. 浮点数的计算公式为:(-1)^S * 1.M * 2^(E-127)

.浮点数的分布理解

理解这个的关键就是,理解浮点数的小数点是浮动的,浮点数不是一个点,而是代表了一个范围。

单精度浮点数的指数范围就是[-127,128],那么当数字绝对值越小时,它可以移动的小数点产生的数字就越多,因此越靠近中间能表示的数字就越多。

参考文章:

维基百科-IEEE754

深入理解浮点数有效位,浮点数分布

知乎-计算机中的浮点数在数轴上分布均匀吗?

声明:Eironn's Blog|版权所有,违者必究|如未注明,均为原创|本网站采用BY-NC-SA协议进行授权

转载:转载请注明原文链接 - IEEE 754标准和浮点数学习


Java开发,同时会一些旁门左道。