三:计算机进行小数运算时出错的原因

0、开篇:

(1)二进制数0.1,用十进制数表示的话是多少?
     0.5
(2)用小数点后有3位的二进制数,能表示十进制数0.625吗?
     可以,0.101
(3)将小数分为符号、尾数、基数、指数4部分进行表现的形式称为什么?
     浮点数(浮点数形式)
(4)二进制数的基数是什么?
     2
(5)通过把0作为数值范围的中间值,从而在不使用符号位的情况下来表示负数的表示方法称为什么?
     EXCESS系统表示(EXCESS是“剩余的”的意思。例如,把01111111看作是0的话,比这个数小1的01111110就是-1)
(6)10101100.01010011这个二进制数,用十六进制数表示的话是多少?
     AC.53

1、将0.1累加100次也得不到10

2、用二进制数表示小数

  先来一个热身,把1011.0011这个有小数点的二进制数转换成十进制数(前面章节已经讲过了用二进制来表示整数的方法,其实小数也一样),如下图所示:

  

3、计算机运算出错的原因

   通过第2节的学习,大家应该也能大概知道计算机运算出错的原因了:是因为“有一些十进制数的小数无法转换成二进制数”。例如十进制数0.1,就无法用二进制数正确表示,小数点后面即使有几百位也无法表示。

      我们就看小数点后4位用二进制数表示时的数值范围0.0000~0.1111,如下图所示:

  

  这里只能表示0.5 、 0.25 、 0.125 、0.0625这四个二进制数小数点后面的位权组合而成(相加总和)的小数。再回到我们要表示的0.1,怎么加都无法加到0.1的,只会变成0.00011001100...(1100的循环),不信你可以去试试。

       说到这里,大家应该都能明白为什么将0.1累加100次也得不到10的原因了把,因为无法正确表示的数值,最后都变成了近似值。计算机是一个功能有限的机器设备,是无法处理无限循环的小数的,一般计算机都会从中间截断。

4、什么是浮点数

  像1011.0011这样带小数点的表现形式,完全是纸面上的二进制数表现形式,在计算机内部是无法使用的。这一章节主要是说明计算机是以什么样的表现形式来处理小数。很多编程语言中都提供了两种表示小数的数据类型,分别是双精度浮点数与单精度浮点数。双精度浮点数类型用64位、单精度浮点数用32位来表示全体小数。从C语言中,双精度浮点数类型和单精度浮点数类型分别用double和float来表示。

  浮点数是指符号、尾数、基数和指数这四个部分来表示的小数,如下图:

  

  因为计算机内部使用的是二进制,所以基数自然就是2.因此,实际的数据中往往不考虑基数,只用符号、尾数、指数这三部分即可表示浮点数。也就是说,64位(双精度浮点数)和32位(单精度浮点数)的数据,会被分为三部分来使用,如下图展示:

  

  ① 符号部分:是指使用一个数据位来表示数值的符号。该数据位是1时表示负,为0时则表示“正或者0”。
     ② 尾数部分:数值大小的决定部分之一(另外一个是指数部分)。尾数部分用的是“将小数点前面的值固定为1的正则表达式”。
     ③ 指数部分:用的则是EXCESS系统表现。

5、正则表达式和EXCESS系统

   ① 尾数部分使用正则表达式,可以将表现形式多样的浮点数统一为一种表现形式。例如十进制的0.75就有很多种表现形式:

   

      正因为表现形式太多,计算机处理时就会比较麻烦。因此,为了方便计算机处理,需要制定一个统一的规则。例如,十进制数的浮点数应该遵循“小数点前面是0,小数点后面第1位不能是0”这样的规则。根据这个规则,0.75就是“0.75 * 10的0次幂”,也就是说,只能用尾数部分是0.75、指数部分是0这个方法来表示。根据这个规则来表示小数的方式,就是正则表达式。
     二进制数也是同样的道理,在二进制数中,我们使用的是“将小数点前面的值固定为1的正则表达式”。具体来讲,就是将二进制数表示的小数左移或右移(这里是逻辑移位。因为符号位是独立的)数次后,正数部分的第1位变为1,第2位之后都变为0(这样是为了消除第2位以上的数位)。而且,第1位的1在实际的数据中不保存。由于第1位必须是1,因此,省略该部分后就节省了一个数据位,从而也就可以表示更多的数据范围。
下图是单精度浮点数的正则表达式的具体例子(单精度浮点数中,尾数部分是23位):

  

6、在实际的程序中进行确认

  对EXCESS系统与正则表达式了解了吗,如果还没有了解,就返回上一小节再看一看,我也是看了几遍才看懂的,如果看懂了,就往下看,用C语言程序来确认单精度浮点数表示方法。

7、如何避免计算机计算出错 

   计算机计算出错的原因之一是采用浮点数来处理小数(另外,也有因“位溢出”而造成计算错误的情况)。作为程序的数据类型,不管是使用单精度浮点数还是双精度浮点数,都存在计算出错的可能性。接下来会介绍两种避免该问题的方法。
     ① 回避策略,即无视这个错误,只要得到近似值就可以了,那些微小的误差完全可以忽略掉。
     ② 把小数转换成正数来计算。

8、二进制数和十六进制数 

   最后补充说明一下二进制数和十六进制数的关系。在实际程序中,如果二进制位数太多,看起来会比较麻烦,因此,在实际程序中,也经常会用十六进制数来代替二进制数。二进制数的4位,正好相当于十六进制数的1位。例如,32位二进制数00111101110011001100110011001101用十六进制数来表示的话,就是3DCCCCCD这个8位数。由此可见,通过使用十六进制数,二进制数的位数能够缩短至原来的1/4 。
     用十六进制数来表示二进制小数时,小数点后的二进制数的4位同样相当于十六进制的1位。不够4位时用0填补二进制数的低位即可。例如,1011.011的低位补0后为1011.0110,这时就可以表示为十六进制数B.6 。
 

 

posted @ 2017-07-29 18:00  Helius-黑牛  阅读(1485)  评论(0编辑  收藏  举报