進制的故事
跟數字打交道時我們接觸的最多的是十進制了,我們從小學到大學的數學課基本上都用的十進制.但實際上還存在各種各樣的其他進制.比如時間就是60進制,中國古代用十六進制表示重量,十六兩是一斤,所以有成語半斤八兩.
而我們現在普遍使用的電腦跟二進制是離不開,可以說如果沒有二進制的發明就沒有計算機如今的普及,這就得感謝萊布尼茲這樣的牛人了啊,其實計算機領域真正的牛人基本上是搞數學的.畢竟計算機的理論基礎就是數學嘛.通過二進制這麼簡單的東東能演化出電腦這麼復雜的功能,真的讓人覺得這個世界真神奇啊.簡單就是美,這是很多科學家的座右銘.很多偉大 的理論就只是一個簡單的數學式子,比如牛頓力學三大定律,愛因斯坦質能方程.人的基因其實組合也很簡單,就是一對對的鹼基對,但最終組合出來的結果復雜的嚇人啊.
二進制不一定是最好的
二進制理論上是非常完美的,當其實實現起來還是不太好,隨便表示個啥東東都要那麼多晶體管組合出來.但由於現在技術的限制,晶體管只能通過開關兩種穩定的狀態來表示0,1兩個數字.假如晶體管能夠表示8種狀態那多爽啊,這樣以前要用八個晶體管表示一個字節,現在只要一個就搞定,成本降低了八倍.這樣的話二進制就再沒任何用處,直接用八進制就行.如果一個晶體管能表示10個狀態,那更完美.直接套用我們習慣的十進制,不用再用煩人的二進制.可惜理想是美好的,現實是殘酷的.暫時沒法做到那樣.不過有很多人現在在研究量子計算機,一個量子可以表示很多種狀態.具體多少不知道,反正肯定大於2.另外除了能表示多種狀態外,還有些其他的特性,可以給計算機帶來更強大的功能.不過現在只是研究階段,要真正實現並推向市場不知道哪年哪月去了.
為啥用八進制和十六進制
在計算機裡用二進制我們比較容易理解,那為啥還有八進制,十六進制呢? 其實只是為了表示方便,我們看起來順眼,交流方便.一連串的01誰都看的暈啊.反正不管你搞多少進制,計算機最後識別的就二進制.那有人說為啥不直接用十進制得了啊,為啥整個怪怪的八,十六出來啊.我覺得用十進制是完全可以的.不過我覺得八進制,十六進制也有些優勢.比如轉換起來比較方便.我們知道十進制轉行成二進制要用啥輾轉相除法,如果位數比較多就較麻煩.而八進制的話,只要把每一位替換成對應的三位二進制,然後順序不變,就OK了.十六進制就是每一位替換成對應的四位二進制.反過來的話二進制轉八進制就是從右向左每三個一組,左邊最後一組不足三就補0,然後每一組轉換成八進制數就行.二進制轉十六進制就也一樣,只不過四個一組.另外十六進制的話表示的數字會比十進制更短.所以我們平時用十六進制較多點,八進制現在用的少了.
在C++中表示八進制的字面值是前面加個0(數字0),十六進制是0x(數字零和字母x).例如 int eight = 011; //十進制9 int sixteen = ox10;//十進制16
在C#中就沒有八進制的字面值表示了int eight = 011;//十進制就是11而不是9; 十六進制是跟C++一樣
微軟的官方文檔裡是這樣說的,只有十進制和十六進制的字面表示了
Integer literals are used to write values of types int, uint, long, and ulong. Integer literals havetwo possible forms:decimal and hexadecimal.
正負數二進制表示
我們知道怎麼把十進制數轉化成二進制數,但具體在計算機中怎麼表示呢? 這裡要說下程序運行時內存中表示和硬盤實際保存時的區別.硬盤保存啥文件,基本上全是當字符處理,每一個數字對應的是字符數字,現在流行unicode,就把每個字符用unicode中對應的數字表示.最後這些數字全轉化成01串.如果裡面的內容代表其他啥特殊意義都是應用程序讀取後作另外的處理了.
像我們表示數據類型時用啥int,long之類的說的是在內存中的表示.舉個簡單的例子吧.用一個字節的類型舉例,C++中的char,C#中的byte.
假如unsigned char ch = 128; //由於是無符號的數字,不用關心正負號,用八個01串全部表示數字.於是在內存中就是10000000.也就是1後面7個0.
補碼表示負數
但如果有符號時咋整啊.就用8位中的第一位表示符號,0表示正數,1表示負數.剩下的7位表示具體數字.這樣char類型只能表示-128至127這個范圍內的數字了.那根據這種轉換規則前面的數字10000000表示多少呢?第一位是1表示負數,後面全0那自然是0,於是這數是-0? 其實這個不一定,要看CPU的指令集怎麼設計,有些計算機可能確實這麼處理,這麼表示.不過大部分不是這樣.大部分計算機表示負數時不這樣直接表示,而是把負數轉換成它對應的補碼.這樣的好處是以後不用再管負數和減法了,只要考慮加法就行,兩數相減就是一個數加一個負數.
補碼的轉換是這樣的,先把負數的絕對值所有位取反,然後加1.
舉個例子,-128.它的絕對值是128,128是10000000(這裡不用考慮符號的事).再取反就是01111111,然後再加1就是10000000(怎麼變回去了啊,-128,跟128一樣了啊.這只是巧合,127和-127就不一樣的.於是-128在內存中表示和128一樣都是10000000.不過應用程序讀取出來肯定不會當成一樣的.應用程序還有這個數字的其他信息,如果知道它是無符號數就當成128,如果是有符號數就是-128
所以在內存中兩個一樣的值,被你讀出來時看你當成什麼類型,不同的類型就被轉換成不一樣的結果.有時結果一樣,有時不一樣.
你可以驗證下,unsigned char num = 128; char ch = (char)num; //ch是-128.而不是期望的128.所以我們一般會說某種類型只能表示某個范圍內的數字,超出了就是值溢出,會得到個錯誤的結果.
整數的表示比較簡單點,浮點數的表示就非常復雜了.這裡先不討論.