什麼是浮點數?html
在數據類型中寫道,浮點數是帶小數點的小數,這個概念是不許確的;浮點數是除了無限不循環小數以外的小數,也就是能夠用分數表示的帶小數點的數。spa
好了,浮點數就這些內容,講完了,各回各家,各找各媽。code
不想回家的就繼續往下看咯。。。htm
雖然浮點數中沒有無限不循環小數可是有無限循環小數啊,計算機內存是有限的是怎麼放下無限多個數的?blog
很簡單啊,既然放不下這麼多我就四捨五入一下把它變成能夠放下的有限小數。內存
那這就涉及到的怎麼對無限循環小數和超出計算機內存儲存範圍的有限小數進行四捨五入,在小數點後幾位小數進行四捨五入。get
咱們就須要一個標準對上述等問題進行規範,已經有人給出了這個標準,IEEE給出了IEEE 754 數學
IEEE和IEEE 754是什麼東西?既然都這麼問了,那就給出百度百科的連接好了。it
n=0 for i in range(10): n+=0.1 print(n) # 打印結果 # 0.1 # 0.2 # 0.30000000000000004 # 0.4 # 0.5 # 0.6 # 0.7 # 0.7999999999999999 # 0.8999999999999999 # 0.9999999999999999
不對啊,0.1是有限小數啊,不用四捨五入啊!幹嗎會出現這樣的鬼結果。class
嘿嘿,這個怪我。
上文所說的的「計算機內存是有限的是怎麼放下無限多個數的」’是我故意誤導的,計算機儲存信息是經過二進制數進行儲存的,這個浮點數也要轉換二進制才能夠儲存在計算機中,0.1的二進制表示是無限循環的。
因此舍入操做針對的是浮點數轉換的二進制數,且舍入的規則也就變成了0舍1入,IEEE 754 中文叫作IEEE二進位浮點數算術標準。
在這也順帶科普一下,小數如何轉換爲二進制數。
是否是很爽,原來浮點數就是這麼被放的計算機裏面去的,那你又錯了。
二進制小數裏的小數點怎麼存進計算機的內存中?(計算機儲存數據都是用二進制來表達)
爽不爽,是否是有個人衝動,嘿嘿。解決方法也在IEEE 754中
IEEE 754 中規定了四種表示浮點數值的方式:單精確度(32位比特儲存浮點數)、雙精確度(64位比特儲存浮點數)、延伸單精確度(43位以上,不多使用)與延伸雙精確度(79位以上)。
C語言的float一般是指IEEE單精確度,而double是指雙精確度,而Python中只有雙精確度(float)。
如今只說說雙精確度(64位比特儲存浮點數),下文看懂了,剩下的三種都天然而然就懂。
你們還記得科學計數法嗎?不記得的問百度去,省得壓不住小學數學老師的棺材板。
十進制 0.75 科學計數法 7.5*10^-1
十進制 2.75 科學計數法 2.75*10^0
類比
二進制 0.11 科學計數法 1.1*2^-1
二進制 10.11 科學計數法 1.011*2^1
能夠得出一個通式
S爲 0 or 1
1<=M<2
E是整數
如今咱們就開始分配那儲存浮點數的64位比特
最高的1位比特(bit)Sign儲存的是符號位S,(-1)^S表示符號(正負號,S=0正號,S=1爲負);
接着的11位比特(bit)Exponent儲存的是2^E冪運算的指數E,固然E的儲存也要轉換爲二進制數進行,(E爲負數小數點向前移|E|位,爲正小數點向後移|E|位);
剩下的52位比特(bit)Significant儲存有效數字M(這也是Python的浮點數大約精確的17位的緣由,爲何,本身想去)。
E轉換爲二進制數儲存,和浮點的儲存不一樣
E有正負,E轉換爲二進制儲存,不像浮點數同樣,用單獨1位比特(bit)來儲存符號,它是藉助一箇中間值,來實現正負數的表達。
中間值:11位比特(bit)Exponent儲存的是E,因此E的取值範圍理論上是 [0,2047] 2^11-1=2047 中間值=1023(取值範圍最中間的那個數)
IEEE754規定中間值1023那個位置表示E=0,小於中間值爲負數(小多少就是負多少),大於中間值爲正數(大多數就是正多少),以後在將通過中間值處理的數轉換爲二進制,存在Exponent中
例如:E=11
有效數M的儲存也本身的規則(也是能夠儲存小數點的緣由)
IEEE 754規定Significant儲存的只是M中小數點後的數(存了52位小數點後的二進制數,因此應該存了52位有效數包括開頭的1),
由於M的都是以1.開頭的(別問我爲何,說明你二進制科學計數法,還不是很明白),因此只要在取出Significant儲存的二進制數前面加上1.(IEEE 754就是用這個方法繞開儲存如何小數點問題的)。
講了這麼多,可是仍是沒講到痛點啊!
0.1連續的相加10次爲何不是1,而是0.999…
走過路過不要錯過啊,止痛良藥來了。
咱們就用上述的IEEE754的標準把十進制0.1儲存進計算機內存中
0011(0舍1入)
S=0 E=1111 1110 11 Significant中存的是 1 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 010
64位比特(bit)存: 0 1111 1110 11 1 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 010
提取內存中剛存進去的0.1的二進制表達式
Significant中存的是 1 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 010 前面添加 0.0001就是提取出來的二進制0.1(由於E=-4,且IEEE 754幫你存着1.)
因此從內存提取出來的0.1的二進制是 0001 1 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 010
下面是計算二進制小數轉十進制小數的腳本
import re a=re.sub(' ','','00011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 010') # 去空格 n=0 sum=0 for i in a : n-=1 sum+=int(i)*2**(n) print( "%.17f" %sum) # 格式化 # 輸出結果 # 0.10000000000000001
最後提一個問題,E轉換爲二進制時,爲何要有中間值的方式,這樣更帥?
對哦,中間值的轉換爲二進制恰好爲11個1,1111 1111 111哦。。。。
歡迎評論,番茄,雞蛋都砸過來吧!!!