浮点数在计算机中的表示

浮点数在计算机中的表示

 

最后编辑于:2010-4-13
计算机中数字是以0和1二进制保存的,我们熟悉的是整数的如何在计算机中表示,那么浮点数是如何表示的呢?

一.    转换
我们先来看看如何将十进制的浮点数转换成二进制。
一个十进制的浮点数,例如:abcd.efg  (其中a~g为0..9),其值用多项式为:
a*10^3 + b*10^2 + c*10^1+d*10^0+e*10^(-1)+f*10^(-1)+g*10^(-3)。

而一个二进制的浮点数,我们也将其表示成:abcd.efg  (其中a~g为0或1),其值表示为:
a*2^3 + b*2^2 + c*2^1+d*2^0+e*2^(-1)+f*2^(-1)+g*2^(-3)。

我们看到底由十进制时的10换成了二进制时的2了,其它都一样。所以一个十进制的浮点数转换成二进制必须分两步进行:整数部分和小数部分。
1.    对于整数部分,和以前的整数转换是一样的。
2.    对于小数部分,比较特殊。下面讲两种转换方法。
方法一:依次与2^(-n)作比较(n从1开始),若大于该值则为1,且减去此值,否则为0;然后继续下一轮比较。举例说明:将0.842356转换成二进制:

 

此时,你会发现比较将会是无穷无尽的。如果你截取到某位,必须做一些取舍。取舍的标准是:其后一位若为1则进1;后一位为0则不进。 还是以上面为例,若要截取9位,因为第10位为0,故不进位,则最终的结果为:0.110101111 ;若要截取到8位,因为第9位为1,故要进位,则最终的结果为:0.110110000 (即0.1101101111 + 0.0000000001)。从这个例子可以看出十进制小数的转换成二进制时只是一个近似值。其实大部分浮点数保存在计算机中都只是一个近似值。至于是稍微大于原值还是稍微小于原值,要看截取时有无进位。

方法二:若在计算机中计算方法一的过程,因为2^(-n)本身就是一个浮点数,而浮点数之间的比较和计算难免有误差。所以我想到了下面这个方法:
1)    首先生成首数字为1、后面0的个数为小数位数的基准数,比如0.254的基准数为1000、0.00353的基准数为100000。
2)    将小数部分乘以上面的基准数,这样得到一个整数。
3)    对该整数乘以2,若积大于基准数,则为1,同时将积减去基准数后得新的整数;若积小于基准数,则为0。
4)    用新的整数重复步骤3,直到整数为0或者到需要的精确位数,作取舍后结束。

举例说明:将0.842356转换成二进制,基准数为1000000,转换成整数为842356,

 

此法可以有效地避免浮点数的比较,能方便且快捷地获得对应的二进制值。

二.    存储
现在已转换成浮点二进制了,那么如何在计算机中表示呢?这要说到科学计数法,这个大家比较熟悉。十进制的科学计算法可以表示成如下:(-1)^s * M * 10^E ,S表示符号:S为1表示负数;0为正数。M成为尾数,其范围为1<=M <10 。E被成为幂,也叫指数。

二进制的科学计数法,也是IEEE的浮点数标准格式,和十进制格式一样:(-1)^s * M * 2^E 。M的范围1<= M <2。将一个二进制浮点数转换成科学计算法很简单,例如:
1)10001.110001 小数点左移4位后成 (-1)^0 * (1.0001110001) * 2 ^ 4.
2) -0.000010001 小数点右移4位后成(-1)^1 * (1.0001) * 2 ^ ( – 4)

在计算机中,表示浮点数由两种常用的格式:单精度浮点数和双精度浮点数,它们在精度上有所差别,同时所需要的空间也有差别:

 

1)    当为负数时,符号位为1,否则为0。
2)    指数有正数亦有负数,这里保存时使用了加偏移量的方法:8位指数位的指数范围为-127~128,其偏移量为127;11位指数范围为-1023~1024,其偏移量为1023。保存时指数加上偏移量,可以避免负数问题;取值时再减去偏移量就行了。
3)    因为尾数1<=M<2,就是说小数点前面总是有一个1。为了节省空间,将此处的1省去,直接将小数点后面的部分放入到小数部分(这也是这部分为什么叫“小数部分”,而不是“尾数部分”的原因)。

举一例:将12.842356保存成单精度浮点格式。
1)    首先将它转换成二进制格式:1100.11010111101001001010,后面位直接截去。
2)    转换成科学计数法格式:1. 10011010111101001001010。右移3位于是指数为3+127=130,二进制为10000010。
3)    于是符号位为0,指数为:10000010,小数去掉前面的1后为10011010111101001001010。这些二进制就是最终保存在电脑里的格式:0 10000010 10011010111101001001010,十六进制格式为:0x414D7A4A。

三.    SQL SERVER中的浮点数
SQL SERVER浮点数有float和real两种。Float类型定义格式:float [ ( n ) ]。 n 为用于存储科学记数法 float 数尾数的位数,同时指示其精度和存储大小。n 必须为从 1 到 53 之间的值。
   精度与存储字节之间的关系:


在 SQL Server 中,real 的同义词为 float(24)。


比起其他的精确数值类型,浮点数的优点是保存的数值大。比如单精度由于其指数为8位,故其最大值约为1.1 * 2^128 = 3.4 * 10^38;双精度其指数为11位,故其最大值约为1.1 * 2^1024 = 1.79 * 10^308。

当然,浮点数的最大缺点就是数据的近似值保存。就是这个原因,导致了下面的一个小小的问题。
四.    SQL SERVER中浮点数的一个问题
因为float和real格式只能保存近似值,除非要保存特别大的数值,否则推荐使用精确数值类型。下面说说在SQL SERVER中遇到的一个浮点问题,其实这也是为什么写这篇文章的原因。最近有人问下面这个问题:

Declare @a, @b float
Set @a = 1.465
Set @b = 2.465
Select round(@a, 2), round(@b, 2)

结果分别为1.47和2.46,这是为什么?
上文中我们说过,大部分浮点数保存在计算机中都只是一个近似值。小数部分转化成二进制时,会根据情况截去或进位。
1.465保存到计算机中时,小数的最后一位需要进位(结果为0x3FF770A3D70A3D71),这就造成了保存的值比实际的1.465稍微大一点。0x3FF770A3D70A3D71转换成十进制后值约为1.4650000000000001。
而2.465却正好相反,它在保存到计算机中时,小数的最后一位后面直接截掉(结果为0x4003B851EB851EB8),这就造成了保存的值比实际的2.465稍微小一点。0x4003B851EB851EB8转换成十进制后值约为2.4649999999999999。

Round函数只是对保存的值进行了四舍五入,没有任何问题。若选择使用精确数值类型,可以有效地避免这种问题的发生。

五.    参考文献
1.    http://blog.csdn.net/YUKUILONGQQ/archive/2008/10/27/3157627.aspx
2.    http://blog.sina.com.cn/s/blog_4c7fa77b01000ai2.html

 

我写了一个程序,模拟浮点数的内存格式,下载地址为:http://download.csdn.net/source/2233150

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值