第一次写python,真是蛇年学python的节奏。

在本程序中想进行如下一个循环,并在最后一层中进行一个if判断:当ini_allocation中得元素之和为1时进行下面的part。



1  for i in range(0,11):
 2      ini_allocation[1] = 0.0
 3 
 4     for j in range(0,11):
 5          ini_allocation[2] = 0.0
 6 
 7          for k in range(0,11):
 8              ini_allocation[3] = 0.0
 9 
10              for ii in range(0,11):
11                  if ini_allocation[2]+ini_allocation[3]+ini_allocation[1]+ini_allocation[0] == 1.0:



#每个ini_allocation在循环末尾都会递增0.1

在运行的过程中,发现ini_allocation无法遍历到[0.0, 0.0, 0.2, 0.8]以上,也就是ini_allocation[3]递增到0.8时,if语句的逻辑值已经为false。

为此,继续采用以下代码进行debug



1             for ii in range(0,11):
2                 if ini_allocation == [0.0, 0.0, 0.2, 0.8]:
3                     print "here"
4                 if ini_allocation[2]+ini_allocation[3]+ini_allocation[1]+ini_allocation[0] == 1.0:
5                     print "true"
6                 else:
7                     print "false"



在terminal中输出结果,既没有“here”,也没有“true”。ini_allocation[3]根本不等于0.8啊,且ini_allocation之和不为1.0. 额,难道0.2+0.8不等于1.0么?

 

虽然python能很好地支持浮点数运算,但是python中浮点数是用二进制分数进行表示的。以上代码中得浮点数是由十进制分数表示,因此在其转换为二进制分数时面临着尾数上的截断误差。为什么会有截断误差呢?首先要从十进制小数转换为二进制小数的方法说起,

已知十进制小数B, 可以将B展开为2的负指数幂的级数,有

B=a*(2^-1)+b*(2^-2)+c*(2^-3)+...

那么将B转换为二进制小数时,只需要进行以下的步骤,其中A代表的是二进制小数的小数部分,i从小数第一位开始计数:



B = B*2
i = 1
LOOP 
  IF B > 1
    A[i]=1
  ELSE
    A[i]=0
  i++
END



大部分小数B不能由2的负指数幂的有限级数表示,如0.7,其二进制小数位无线循环小数。但是计算机的位数有限,无法存储无限位的小数,因此会在小数末尾位将不能存储的部分进行截断,产生截断误差。

最初的程序中,从0.0递增到1.0,不免会遇到0.5,0.7这类二进制下无限循环小数,产生截断误差。而python只会呈现(display)出这些小数的近似值,因此我们的ini_allocation[3]可能和0.8不等,而且理论上误差是随意的。

如何解决这样的问题呢?对于精度要求不高的程序,如本程序只要求精确到小数点第一位,那么我们调用decimal模块提高浮点数运算的精度,然后将精确位以后的小数位给“人工截断”



from decimal import *



利用round函数进行截断操作,该函数第一个变量是需要截断的浮点数,第二个是精确的位数。



1    sum2 = Decimal(ini_allocation[0])+Decimal(ini_allocation[1])+Decimal(ini_allocation[2])+Decimal(ini_allocation[3]) # float calculation
2    sum2 = round(sum2,1) # reduce the precision to 1 deci



通过这样,就消除浮点数对本程序的影响了,这样0.8+0.2=1.0了。

 

References:

http://docs.python.org/2/tutorial/floatingpoint.html   the float-number in python
http://docspy3zh.readthedocs.org/en/latest/tutorial/floatingpoint.html   the float-number in python
   decimal and round()
http://baike.baidu.com/view/1426817.htm   10-binary to 2-binary