浮点数原理探究

##一、文章来由 今天听到实验室有人问道浮点数的实现机制,之前刚好研究过原码、反码、补码、移码的关系,而这类问题很底层,一般容易忽视。干脆打破沙锅问到底,彻底搞清楚这个问题并留下证据,于是就有了这篇博文。

##二、原码、反码、补码、移码 不要背复杂的公式,简记如下,都是用的最简单易懂的语言: 特别注意:首先要说明的是,正数的原、反、补码都一样;0的原码跟反码都有两个,因为这里0被分为+0和-0。

原码:

即直接的二进制表示,最高位为符号位:正数为0,负数为1 例如: X=+101011 , [X]原= 00101011
X=-101011 , [X]原= 10101011 位数不够的用0补全。

反码:

正数的反码与其原码相同;负数的反码是对其原码逐位取反,但符号位除外。 例如:X=-101011 , [X]原= 10101011,[X]反=11010100

补码:

正数的补码与其原码相同;负数的补码是在其反码的末位加1。 例如:X=-101011 , [X]原= 10101011,[X]反=11010100,[X]补=11010101 注:补码还有一种速算法,符号位不变,从原码低位开始从右向左数,直到遇到第一个1,保留这个1,以后的按位取反

PS:0的补码是唯一的,如果机器字长为8那么 [0]补=00000000

移码【最简单】:

不论正负数,只要将其补码的符号位取反即可。

例如:X=-101011 , [X]原= 10101011 ,[X]反=11010100,[X]补=11010101,[X]移=01010101

##三、从定点到浮点

首先定点到浮点是一个飞跃,这里面的内容其实可以很简单也可以很复杂。我们一般说的整数都是定点整数,即小数点固定最后一位。但是整数既可以是整数,也可以是浮点数,例如255 是整数,而255.0 则是浮点数。

什么是浮点数?这需要从小数的表示讲起:

###3.1 浮点数的表示

计算机中一个任意进制数 N 可以写成 小数表示法 m :尾数,是一个纯小数。 e :浮点的指数, 是一个整数。
R :基数,对于二进计数值的机器是一个常数,一般规定R 为2,8或16

尾数主要是决定有效位,阶码主要表示位数(小数点位置)。 阶码:用定点整数形式表示,指明小数点在数据中的位置,决定了浮点数的表示范围,常用补码或者移码表示 尾数: 决定了浮点数的数值精度,是定点小数,用补码表示,也决定了整个浮点数的符号

机器字长一定时,阶码越长,表示范围越大,精度越低 浮点数表示范围比定点数大,精度高

画一个简单易懂的表格: c语言float型数表示 符号位(S) | 阶码(E) | 尾数(M) — | — | — 1 | 8 | 23

浮点数表示范围如下图:

浮点数表示范围

例: 8位定点小数可表示的范围 0.0000001 — 0.1111111
1/128 — 127/128

设阶码2位,尾数4位 可表示2-110.0001 — 2110.1111 0.0000001 — 111.1

设阶码3位,尾数3位 可表示2-1110.001 — 21110.111 0.0000000001 — 1110000

注:float和double的范围是由指数的位数来决定的。 float的指数位有8位,而double的指数位有11位,分布如下: float: 1bit(符号位) 8bits(指数位) 23bits(尾数位) double: 1bit(符号位) 11bits(指数位) 52bits(尾数位)

这里写图片描述

###3.2 浮点数的规格化

规格化目的: (1)为了提高数据的表示精度 (2)为了数据表示的唯一性 (3)尾数为R进制的规格化: 绝对值大于或等于1/R

二进制原码的规格化数的表现形式: 正数 0.1xxxxxx 负数 1.1xxxxxx

补码尾数的规格化的表现形式:尾数的最高位与符号位相反: 正数 0.1xxxxxx 负数 1.0xxxxxx

这里写图片描述

【32位浮点数,IEEE754采用127做阶码的偏移量】 IEEE754规定这个偏移量为2^(e-1)-1,e为存储指数的位元的长度,在32位浮点数中存储指数的域有8位因此偏移量位2^(8-1)-1=127。这里根据标准,并没有使用移码作为阶码 注意IEEE754中的移码和通常用的移码不相同,IEEE754用的是127移码,即在原数上加127 而不是通常的128移码(也就是所说的补码符号位取反)

例1:

这里写图片描述

如果反过来求就是, 例2 这里写图片描述

例3 这里写图片描述

###3.3 移码的来由 一个很有意思的问题 说了这么多,由移码当阶码,到移码-1到阶码,都不知道为什么,现在就来看看。 根据百度百科移码上说的:

移码(又叫增码)是符号位取反的补码,一般用做浮点数的阶码,引入的目的是为了保证浮点数的机器零为全0。

原因是:

用补码表示阶码的时候,当阶码无限小,产生了下溢的时候,阶码变成了0,那么这个浮点数的值变为了1。 而实际上这个数是无限接近于零的。那么我们就需要取出其中的 “-0“ 值作为机器零。

详细解释如下: 因为浮点数的表示是1.s * (2 ^ P), 所以浮点数0实际上是取无限接近于0,而不是其数值上确实得等于0

0的表示实际上是1.0 * (2 ^ -128),其中-128是阶码能表示的最小数

于是问题来了,如果用补码表示, -128实际上是10000000, 那么浮点数的0其数值就不能等于0. 所以采用移码.

但是IEEE754标准采用的是127移码,即在0的基础上加上127作为0,于是阶码表示范围是-127 - 128,而不是通常的-128 - 127

文章也基本上要告一段落了,这些应该已经可以对付日常理解,如果需要更深刻的理解,就需要查阅更多资料,如果博文中有不当的内容,欢迎批评指正,也欢迎讨论

附: c++中,合法的浮点数表示形式: (1)十进制小数形式。他有数字和小数点组成,必须有小数点。例如(123.)(123.0)(.123)。 (2)指数形式。如123e3。字母e(或E)之前必须有数字,e后面的指数必须为整数。 (3)规范化的指数形式里面,小数点前面有且只有一位非零的数字。如1.2345e8

详细了解补码,要看我这篇:补码与模

本篇csdn博客链接