第一次寫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
http://blog.csdn.net/unicode1985/article/details/1606067 decimal and round()
http://baike.baidu.com/view/1426817.htm 10-binary to 2-binary