一、文章来由
今天听到实验室有人问道浮点数的实现机制,之前刚好研究过原码、反码、补码、移码的关系,而这类问题很底层,一般容易忽视。干脆打破沙锅问到底,彻底搞清楚这个问题并留下证据,于是就有了这篇博文。
二、原码、反码、补码、移码
不要背复杂的公式,简记如下,都是用的最简单易懂的语言:
特别注意:首先要说明的是,正数的原、反、补码都一样;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-11*0.0001 — 211*0.1111
0.0000001 — 111.1
设阶码3位,尾数3位
可表示2-111*0.001 — 2111*0.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
版权声明:本文为博主原创文章,未经博主允许不得转载。