c++浮點數儲存方式

來源:互聯網
上載者:User
最近一段時間看到版上關於C++裡浮點變數精度的討論比較多,那麼我就給對這個問題有疑惑的人詳細的講解一下intel的處理器上是如何處理浮點數的。為了能更方便的講解,我在這裡只以float型為例,從儲存結構和演算法上來講,double和float是一樣的,不一樣的地方僅僅是float是32位的,double是64位的,所以double能儲存更高的精度。還要說的一點是文章和程式一樣,相容性是有一定範圍的,所以你想要完全讀懂本文,你最好對二進位、十進位、十六進位的轉換有比較深入的瞭解,瞭解資料在記憶體中的儲存結構,並且會使用VC.net編譯簡單的控制台程式。OK,下面我們開始。

       大家都知道任何資料在記憶體中都是以二進位(1或著0)順序儲存的,每一個1或著0被稱為1位,而在x86CPU上一個位元組是8位。比如一個16位(2位元組)的short int型變數的值是1156,那麼它的二進位表達就是:00000100 10000100。由於Intel CPU的架構是Little Endian(請參數機算機原理相關知識),所以它是按位元組倒序儲存的,那麼就因該是這樣:10000100 00000100,這就是定點數1156在記憶體中的結構。

       那麼浮點數是如何儲存的呢?目前已知的所有的C/C++編譯器都是按照IEEE(國際電子電器工程師協會)制定的IEEE 浮點數標記法來進行運算的。這種結構是一種科學標記法,用符號(正或負)、指數和尾數來表示,底數被確定為2,也就是說是把一個浮點數表示為尾數乘以2的指數次方再加上符號。下面來看一下具體的float的規格:

float
共計32位,摺合4位元組
由最高到最低位分別是第31、30、29、……、0位
31位是符號位,1表示該數為負,0反之。
30-23位,一共8位是指數位。
22-0位,一共23位是尾數位。
每8位分為一組,分成4組,分別是A組、B組、C組、D組。
每一組是一個位元組,在記憶體中逆序儲存,即:DCBA

       我們先不考慮逆序儲存的問題,因為那樣會把讀者徹底搞暈,所以我先按照順序的來講,最後再把他們翻過來就行了。

        現在讓我們按照IEEE浮點數標記法,一步步的將float型浮點數12345.0f轉換為十六進位代碼。在處理這種不帶小數的浮點數時,直接將整數部轉化為二進位表示:1 11100010 01000000也可以這樣表示:11110001001000000.0然後將小數點向左移,一直移到離最高位只有1位,就是最高位的1:1.11100010010000000一共移動了16位,在布耳運算中小數點每向左移一位就等於在以2為底的科學計演算法表示中指數+1,所以原數就等於這樣:1.11100010010000000 * ( 2 ^ 16 )好了,現在我們要的尾數和指數都出來了。顯而易見,最高位永遠是1,因為你不可能把買了16個雞蛋說成是買了0016個雞蛋吧?(呵呵,可別拿你買的臭雞蛋甩我~),所以這個1我們還有必要保留他嗎?(眾:沒有!)好的,我們刪掉他。這樣尾數的二進位就變成了:11100010010000000最後在尾數的後面補0,一直到補夠23位:11100010010000000000000(MD,這些個0差點沒把我數的背過氣去~)

       再回來看指數,一共8位,可以表示範圍是0 - 255的不帶正負號的整數,也可以表示-128 - 127的有符號整數。但因為指數是可以為負的,所以為了統一把十進位的整數化為二進位時,都先加上127,在這裡,我們的16加上127後就變成了143,二進位表示為:10001111
12345.0f這個數是正的,所以符號位是0,那麼我們按照前面講的格式把它拼起來:
0 10001111 11100010010000000000000
01000111 11110001 00100000 00000000
再轉化為16進位為:47 F1 20 00,最後把它翻過來,就成了:00 20 F1 47。
現在你自己把54321.0f轉為二進位表示,自己動手練一下!

       有了上面的基礎後,下面我再舉一個帶小數的例子來看一下為什麼會出現精度問題。
按照IEEE浮點數標記法,將float型浮點數123.456f轉換為十六進位代碼。對於這種帶小數的就需要把整數部和小數部分開處理。整數部直接化二進位:100100011。小數部的處理比較麻煩一些,也不太好講,可能反著講效果好一點,比如有一個十進位純小數0.57826,那麼5是十分位,位階是1/10;7是百分位,位階是1/100;8是千分位,位階是1/1000……,這些位階分母的關係是10^1、10^2、10^3……,現假設每一位的序列是{S1、S2、S3、……、Sn},在這裡就是5、7、8、2、6,而這個純小數就可以這樣表示:n = S1 * ( 1 / ( 10 ^ 1 ) ) + S2 * ( 1 / ( 10 ^ 2 ) ) + S3 * ( 1 / ( 10 ^ 3 ) ) + …… + Sn * ( 1 / ( 10 ^ n ) )。把這個公式推廣到b進位純小數中就是這樣:
n = S1 * ( 1 / ( b ^ 1 ) ) + S2 * ( 1 / ( b ^ 2 ) ) + S3 * ( 1 / ( b ^ 3 ) ) + …… + Sn * ( 1 / ( b ^ n ) )

        天哪,可惡的數學,我怎麼快成了數學老師了!沒辦法,為了廣大編程愛好者的切身利益,喝口水繼續!現在一個二進位純小數比如0.100101011就應該比較好理解了,這個數的位階序列就因該是1/(2^1)、1/(2^2)、1/(2^3)、1/(2^4),即0.5、0.25、0.125、0.0625……。乘以S序列中的1或著0算出每一項再相加就可以得出原數了。現在你的基礎知識因該足夠了,再回過頭來看0.45這個十進位純小數,化為該如何表示呢?現在你動手算一下,最好不要先看到答案,這樣對你理解有好處。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.