C語言中位操作符(1)-計算機中的整數表示方法
寫在前面
長久以來,位操作符一直困擾著我,為什麼呢?因為其雖易用,但是我自己卻理解不透徹,用著總覺得有隱患?那麼今天就來詳細地理一下計算機中的位操作符與整數在計算機中的儲存。
本文是作為一個非科班出身程式設計師的自我學習記錄之作,如果能夠在自我提高的同時也能幫助讀友提高,我自是高興;如有任何疑問或者不妥之處,敬請評論指正。
寫著寫著有些太多,故而分兩次寫完吧,本次只討論計算機中整數的表示方法。
計算機中數字的表示方法
我們都知道在計算機中,數是以補碼錶示的,這樣表示定義是什麼呢,而好處又是什麼?
先看定義:
正數的原碼、反碼、補碼是其本身;
負數的原碼是其本身,反碼是除了符號位以外按位取反,補碼則是反碼加1;
舉例說明:
例1
//uint8_t即是無符號8位數,也就是跟char相同的定義
/*0000 1000,這是其原碼反碼和補碼,也就是其在計算機中是這麼儲存的,也就是說計算機中某個位置一定有0000 1000這一段二進位制碼*/
uint8_t x = 0x08;
uint8_t y = x >> 1;//x = 0000 1000 -> y = 0000 0100 = 4;
這裡x的二進位制表示為0000 1000,最高符號位為0,表示是正數。8位無符號數8的原反補碼如下:
原碼:0000 1000
反碼:0000 1000
補碼:0000 1000
正數的原碼反碼補碼都是其本身。
例2
int8_t x = -0x08;//int8_t即是有符號8位數,其最高位為符號位
int8_t y = x >> 1;//x = 1000 1000 -> y = 1000 0100 = -4;
uint8_t z = x >> 1;
8位有符號數-8,其二進位制表示為1000 1000,最高位1表示符號位,表示負數,那麼其原碼反碼補碼為:
原碼:1000 1000
反碼:1111 0111
補碼:1111 1000
負數的原碼是其本身,反碼是除了符號位外按位取反,補碼是反碼加1.
為什麼使用補碼?
由上面的例子就知道了正數與負數的原反補碼的表示方法。那麼隨之而來的一個問題是,為什麼不用直觀的原碼來表示數,而用繁瑣的補碼?
在網上查了很多,大致歸結為如下兩個原因:
最主要原因,使用補碼可以將符號位和其他位進行統一處理,同時減法也可以按加法來進行運算。
這樣帶來了兩個好處:符號位統一到計算中去和減法可以按加法來計算,而這兩個好處都使得CPU設計更容易。
例3
加入考慮符合位的話,那麼很明顯CPU的加減法設計要變複雜,不僅要單獨處理符號位還要判斷除了符號位之外的大小,並且還要考慮溢位時符號位不被沖掉。
那麼假設符號位不單獨考慮呢?
//-8 + 15 = 7, 8-bit depth operation
//原碼運算
1000 1000
+ 0000 1111
= 1001 0111 //-23
//反碼運算,
1111 0111
+ 0000 1111
= 10000 1000 //8,第一位截斷丟掉
// 補碼運算
1111 1000
+ 0000 1111
= 10000 0111//7,第一位丟掉
那麼由上面可以看到,在符號位納入計算的情況下,只有補碼計算的結果是正確的,並且可以將減法按加法計算。
那麼不禁要問,為什麼要這麼設計補碼呢?
這裡涉及到補碼的本質的問題。
那麼首先,-8,它就是0-8的結果,按8-bit 2進製表示如下
//87654 3210//bit
11111 //表示借位,與10進位制相同,只不過2進制中借1位表示2
0000 0000
- 0000 1000
=11111 1000
由於我們要向更高位(8th-bit借位),並且在結果中會丟掉超出範圍的位(上面結果中最高位的1),那麼上述結果其實等於1 0000 0000 - 0000 1000,即
//87654 3210//bit
11111 //表示借位,與10進位制相同,只不過2進制中借1位表示2
10000 0000
- 0000 1000
=01111 1000
當丟掉最高位後,那麼1 0000 0000 - 0000 1000 其實與0000 0000 - 0000 1000 的結果是一樣的!
而1 0000 0000 = 1111 1111 + 1.那麼求-8也就變成了1111 1111 - 0000 1000 + 1,也即是:
1
1111 1111
- 0000 1000
= 1111 0111//在該結果上加1,即得1111 1000
在二進位制中,如果用w-bit全1的一個值(
所以負數的補碼就是對其除符號位外按位取反再加1。其實對於補碼,另外的一個求法跟這個是等價的,就是一個負數-x(x>0)的補碼就是對x按位取反(包括符號位),再加1。因為x是正的,其符號位肯定是0,那麼按位取反後就是1(表示負的)。
那麼這裡再說負數補碼的本質,-x(x>0)在w-bit下的補碼就是
為什麼補碼適用於正數的加法?
那麼還有一個疑問,補碼為什麼會適用於正數的加法呢?
實際上,我們要證明的是,X-Y或X+(-Y)可以用X加上Y的2的補碼完成。
Y的2的補碼等於(11111111-Y)+1。所以,X加上Y的2的補碼,就等於:X + (11111111-Y) + 1
我們假定這個算式的結果等於Z,即 Z = X + (11111111-Y) + 1
接下來,分成兩種情況討論。
第一種情況,如果X小於Y,那麼Z是一個負數。這時,我們就對Z採用2的補碼的逆運算,求出它對應的正數絕對值,再在前面加上負號就行了。所以,
Z = -[11111111-(Z-1)] = -[11111111-(X + (11111111-Y) + 1-1)] = X - Y
第二種情況,如果X大於Y,這意味著Z肯定大於11111111,但是我們規定了這是8位機,最高的第9位是溢位位,必須被捨去,這相當於減去100000000。所以,
Z = Z - 100000000 = X + (11111111-Y) + 1 - 100000000 = X - Y
這就證明了,在正常的加法規則下,可以利用2的補碼得到正數與負數相加的正確結果。換言之,計算機只要部署加法電路和補碼電路,就可以完成所有整數的加法
參考文獻:
相關推薦
C語言中位操作符(1)-計算機中的整數表示方法
寫在前面 長久以來,位操作符一直困擾著我,為什麼呢?因為其雖易用,但是我自己卻理解不透徹,用著總覺得有隱患?那麼今天就來詳細地理一下計算機中的位操作符與整數在計算機中的儲存。 本文是作為一個非科班出身程式設計師的自我學習記錄之作,如果能夠在自我提高的同時也能幫
C語言之指標筆記(1)
指標(pointer)是C語言中最重要的概念之一,用於儲存變數的地址。 1.&運算子 (1)一元&運算子可給出變數的儲存地址。如過pooh是變數名,那麼&pooh是變數地址。 (2)如下例所示,使用運算子檢視不同函式中的同名變數儲存在什麼位置。 原始碼: //檢視
C語言實現各種排序(1)
#include<stdio.h> //排序分為插入排序,希爾排序,氣泡排序,快速排序,選擇排序,堆排序,歸併排序,基數排序(桶排序) //1.1 //直接插入排序,較為簡單,思路為從第二個數開始逐個將其插入前面有序的序列中 //平均時間複雜度為O(N2),最快時間複雜度為O(N),最
c語言表白用程式碼(1)
不多說,直接上程式碼,有用拿走,侵權立刪。 希望大家儘早找到自己的另一半。 #include <stdio.h> #include <math.h> #include <stdlib.h> #define I 20 #defi
c語言面試知識點彙總(1)
整型和長整型的區別 在早期的計算機,16編譯器, 整型是16位,長整型是32位的。如今,在C99規定 整型和長整型都是32位。 2.編譯系統的四個步驟: 預處理,編譯,彙編,連結 3.For(;;)和while(1)無限迴圈的區別 Fo
【C語言簡單說】二:第一個C語言程式詳解(1)
如有錯誤請給與糾正… 上一個教程只說明瞭第一個C語言程式原始碼中的: printf("Hello Wrold!"); 這行程式碼的含義,現在我們來說說全部程式碼;當然為了各位的方便,我就把那個程式
《手把手教你學C語言》學習筆記(1)---C語言的特點
學習C語言的原因,主要是需要使用C語言程式設計,我用故我學,應該是最主要的原因了。 C語言的定位:C語言嚴格意義上只能算是中級語言,是面向過程程式語言的集大成者,雖然這種語言有很多的問題,但總體
Qt C++與QML混合程式設計(1)- QML中使用C++的類和函式
本章主要介紹一下在QML中使用C++的類的兩種方法 1.設定QML的上下文屬性 為QML的節點設定上線屬性,在QML檔案中可以之間使用這個屬性的函式。 QQuickView view; view.rootContext()->setCo
C語言16位拆成高低八位,和高低八位合成16位資料(1)
#include <stdio.h> #include <stdlib.h> char right,left,temp;//right表示第八位,left表示高八位 int si
C語言占位符(待完善)
tps %u c語言 語言 待完善 csdn 有效 article 指數 %c 讀入一個字符 %s 讀入一個字符串,遇到空格制表符或者換行符時結束。 %d 讀入一個十進制整數 %x或者%X 讀入一個十六進制整數 %o
NET快取框架CacheManager在混合式開發框架中的應用(1)-CacheManager的介紹和使用 基於C#的MongoDB資料庫開發應用(4)--Redis的安裝及使用
在我們開發的很多分散式專案裡面(如基於WCF服務、Web API服務方式),由於資料提供涉及到資料庫的相關操作,如果客戶端的併發數量超過一定的數量,那麼資料庫的請求處理則以爆發式增長,如果資料庫伺服器無法快速處理這些併發請求,那麼將會增加客戶端的請求時間,嚴重者可能導致資料庫服務或者應用服務直接癱瘓。快取方案
.NET快取框架CacheManager在混合式開發框架中的應用(1)-CacheManager的介紹和使用 基於C#的MongoDB資料庫開發應用(4)--Redis的安裝及使用
在我們開發的很多分散式專案裡面(如基於WCF服務、Web API服務方式),由於資料提供涉及到資料庫的相關操作,如果客戶端的併發數量超過一定的數量,那麼資料庫的請求處理則以爆發式增長,如果資料庫伺服器無法快速處理這些併發請求,那麼將會增加客戶端的請求時間,嚴重者可能導致資料庫服務或者應用服務直接癱瘓。快取方案
Cypher查詢語言--Neo4j中的SQL(1)
“Cypher”是一個描述性的圖形查詢語言,允許不必編寫圖形結構的遍歷程式碼對圖形儲存有表現力和效率的查詢。Cypher還在繼續發展和成熟,這也就意味著有可能會出現語法的變化。同時也意味著作為元件沒有經歷嚴格的效能測試。 Cypher設計的目的是一個人類查詢語
C語言輸出素數表(1-100)&前100個
本來是想第二篇做雙鏈表的,今天看C和指標第四章,複習了以下之前做的素數輸出,三種不同方法,結果如下: 1.輸出1-100以內的素數: //這個比較簡單,不多做介紹,唯一注意的一點是for迴圈截止條件
C#如何訪問Lua中的屬性(1)
參考 http://www.myexception.cn/c-sharp/1881698.html C#如何訪問Lua中的屬性 1) .C#如何訪問LUA中的屬性? 2) .C#如何訪問LUA中的函式? 3) .C#如何訪問LUA中的表? 第一個檔案
python中的JSON(1)
welcome 定義 ack () found 存儲 remember nbsp python程序 很多程序都要求用戶輸入某種信息, 例如: 讓用戶存儲遊戲首選項或提供要可視化的數據,程序把用戶的信息存儲在列表和字典等數據結構中, 用戶關閉程序時,我們幾乎總要保存他們提
C語言代碼訓練營(2)
周末 int main 基本 之前 lin 例題 鏈接 params 上一篇的評論中,大家反饋評論中貼代碼無法排版。我們改一下規則,大家可以把自己實現的代碼發布在自己的簡書博文中,之後把鏈接貼在評論中。這樣也方便大家日後追溯。 當然,也可以直接發郵件給我。希望大家堅持打卡,
C語言攻略指南(三)流程控制篇
... cpp 流程控制 printf 循環結構 多重 -a 1-43 continue 流程控制語句,或者說控制流語句,是用於控制程序計算操作執行的次序,使我們能實現判斷,選擇,循環等操作。本篇將逐一描述 C語言中的流程控制語句。 選擇結構 if 語句 if(表達式
XMU C語言程序設計實踐(3)
col stdio.h 元素 ans hide wap 出口 b- 二維 問題描述: 以一個n的長方陣表示迷宮,0和1分別表示迷宮中的通路和障礙,設計一個程序,對任意設定的迷宮,求出一條從入口到出口的通路,或得出沒有通路的結論。 對於本問題需用棧實現“窮舉求解”算法,即:
C語言代碼訓練(一)
color tro 題型 blank adding bsp 喜歡 add 方便 今天我們先來講解一道C語言的經典例題,也是從零開始系列中的一道課後練習題。 請用控制臺程序繪制如下圖案。 循環經典例題 分析情況 這個題目是要求打印30行"*",每行打印的個數不同。通過這