1.引例:
今天在做了一道關於有符號數和無符號數相互轉換及其左移/右移的問題,被它們之間的轉換原理和位移原理搞得頭大了。真的很后悔本科的時候沒有認真學習《計算機組成原理》/《計算機操作系統》等計算機基礎課程。以下是我根據相關知識回顧和整理的材料,如有和某某的文章有雷同之處,請勿見怪。另外也希望看到這篇文章的同志們能夠有所收獲吧。
#include <cstdio> #include <iostream> using namespace std; int main() { unsigned short int ui; signed short int si; ui = (unsigned short int)0x8000u; si = (signed short int)0x8000; printf("ui = %u\n", ui); printf("si = %d\n", si); ui = ui >> 1; si = si >> 1; printf("ui = %u\n", ui); printf("si = %d\n", si); cout << "------------------------------" << endl; ui = (unsigned short int)0x8000u; si = (signed short int)0x8000; printf("%u\n", ui); printf("%d\n", si); ui = ((signed short int)ui >> 1); si = ((unsigned short int)si >> 1); printf("%u\n", ui); printf("%d\n", si); cout << "------------------------------" << endl; ui = (unsigned short int)0x8000u; si = (signed short int)0x8000; printf("%u\n", ui); printf("%d\n", si); ui = ui << 1; si = si << 1; printf("%u\n", ui); printf("%d\n", si); cout << "-------------------------------" << endl; ui = (unsigned short int)0x8000u; si = (signed short int)0x8000; printf("%u\n", ui); printf("%d\n", si); ui = ((signed short int)ui << 1); si = ((unsigned short int)si << 1); printf("%u\n", ui); printf("%d\n", si); return 0; }
顯示結果:
2.概念
在計算機中,可以區分正負類型的數,成為“有符號數”(signed);無正負類型的數(只有整數類型),成為“無符號數”(unsigned)。簡明的說,無符號數就是其所有的位數都用來表示數值的大小,有符號數除最高位來表示數值的正負外(0表示正數;1表示負數),其余各位用來表示數值的大小。舉個例子說明一下:十機制數 正數255 二進制表達形式:1111 1111
十機制數 負數-1 二進制表達形式:1111 1111
可見-1的二進制的最高位為紅色的1,可是為什么其表達形式為1111 1111而不是1000 0001呢?這就關於任何數在計算機內是以補碼形式存儲問題。下面會介紹的,在此先不詳細說明了。
3.存儲范圍
從前面的介紹可以知道,由於有符號數的最高位被拿來用作符號位,所以它所能夠表達的最大數值要小於無符號數所能夠表達的最大數值。還是舉個例子來說明一下吧:
無符號數:1111 1111 十進制值:255
有符號數:0111 1111 十進制值:127
這是有人可能會提出這樣的疑問:有符號數所能夠表達的數值范圍會不會小於無符號數所能夠表達的數值范圍呢?
呵呵,答案是否定的!雖然有符號數在表達最大值上的能力減弱了,但是它能夠表達負數。負數的個數可以彌補其不足。來讓我們比較一下:
一個字節的無符號數的表達數值范圍是:[0,255]
一個字節的有符號數的表達數值范圍是:[-128,0),[0,127]
可見它們都能夠表示256個數。
4.各種碼(原碼/反碼/補碼)
有些人也許會這樣認為"-1"(雙字節)在計算機中的表達形式為1000 0000 0000 0001,可是實際上不是的。計算機是以其補碼的形式進行表達的,即“-1”(雙字節)的表達形式是1111 1111 1111 1111。
說一下各種碼的概念吧。
原碼:一個整數,按照絕對值的大小轉換成二進制數,最高位為符號位。
反碼:將原碼除最高位(符號位)外,其余各位按位取反,所得到的二進制碼。正數的反碼為原碼。
補碼:反碼最低位加1即為補碼。
關於負數的補碼求法說明一下,先得到其反碼,之后將反碼加1即可。有些大神根據其原碼,閉眼即得,這種能力需要修煉一下啊。
這時有些人可能會說,為什么要引入補碼的形式呢?直接按照原碼存儲不就省事很多嗎?嘿嘿,要記住,有些事情並不是你想省事就能省事的。好了來欣賞一下補碼的優勢吧。
計算機的帶符號數用補碼表示的優點:
- 1負數的補碼與對應正數的補碼之間的轉換可以用同一種方法-求補運算完成,可以簡化硬件。
- 2 可將減法變為加法,這樣減法就可以用加法器進行計算了。
- 3 兩個用補碼表示的數相加時,如果最高位(符號位)有進位,則進位被舍棄。
心算求補(大神求補算法):
從最低位開始至找到的第一個1均不變,符號位不變,這之間的各位“求反”(0變1;1變0)。
原碼:1010 1001 補碼:1101 0111.
5.有符號數與無符號數的相互轉換
無符號整數和有符號整數之間進行強制類型轉換時,位模式不改變。
有符號數轉換為無符號數時,負數轉換為大的正數,相當於在原值上加上2的n次方,而正數保持不變。
無符號數轉換為有符號數時,對於小的數將保持原值,對於大的數將轉換為負數,相當於原值減去2的n次方。
當表達式中存在有符號數和無符號數類型時,所有的操作都自動轉換為無符號類型。可見無符號數的運算優先級高於有符號數。
unsigned int a = 20;
signed int b = -130;
運算一下結果是 b>a 。
無符號int與有符號int比較大小,轉化為無符號int來比較
int類型與非無符號int的類型比較時,非無符號int的類型轉化為int來比較
無符號int類型與其他類型如unsigned short,signed short,unsigned char, char 比較時,其他類型一律轉化為無符號int類型來比較
非無符號int類型和非int類型如unsigned short,signed short,unsigned char, char 比較時,一律轉化為int類型來比較
6.轉換大餐
有符號數的轉換
原類型 | 目標類型 | 轉換方法 |
char | short |
符號位擴展 |
char | long | 符號位擴展 |
char | unsigned char | 最高符號位失去位意義,變為數據位 |
char | unsigned short | 符號位擴展到short;然后從short轉到unsigned short |
char | unsigned long | 符號位擴展到long;然后從long轉換到unsigned long |
char | float | 符號位擴展到long;然后從long轉到float |
char | double | 符號位擴展到long;然后從long轉換到double |
char | long double | 符號位擴展到long;然后從long轉換到long double |
short | char | 保留低位字節 |
short | long | 符號位擴展 |
short | unsigned char | 保留低位字節 |
short | unsigned short | 最高為失去意義,變為數據位 |
short | unsigned long | 符號位擴展到long;然后從long轉到unsigned long |
short | float | 符號位擴展到long;然后從long轉到float |
short | double | 符號位擴展到long;然后從long轉到double |
short | long double | 符號位擴展到long;然后從long轉換到long double |
long | char | 保留低位字節 |
long | short | 保留低位字節 |
long | unsigned char | 保留低位字節 |
long | unsigned short | 保留低位字節 |
long | unsigned long | 最高為失去意義,變為數據位 |
long | float | 使用單精度浮點數表示,可能失去精度 |
long | double | 使用單精度浮點數表示,可能失去精度 |
long | long double | 使用單精度浮點數表示,可能失去精度 |
無符號數的轉換
原類型 | 目標類型 | 轉換方法 |
unsigned char | char | 最高為作符號位 |
unsigned char | short | 0擴展 |
unsigned char | long | 0擴展 |
unsigned char | unsigned short | 0擴展 |
unsigned char | unsigned long | 0擴展 |
unsigned char | float | 轉換到long;然后從long轉換到float |
unsigned char | double | 轉換到long;然后從long轉換到double |
unsigned char | long double | 轉換到long;然后從long轉換到long double |
unsigned short | char | 保留低位字節 |
unsigned short | short | 最高為作符號位 |
unsigned short | long | 0擴展 |
unsigned short | unsigned char | 保留低位字節 |
unsigned short | unsigned long | 0擴展 |
unsigned short | float | 轉換到long;然后從long轉換到float |
unsigned short | double | 轉換到long;然后從long轉換到double |
unsigned long | long double | 轉換到long;然后從long轉換到long double |
unsigned long | char | 保留低位字節 |
unsigned long | short | 保留低位字節 |
unsigned long | long | 最高位作符號位 |
unsigned long | unsigned char | 保留低位字節 |
unsigned long | unsigned short | 保留低位字節 |
unsigned long | float | 轉換到long;然后從long轉換到float |
unsigned long | double | 直接轉換到double |
unsigned long | long double | 轉換到long;然后從long轉換到long double |
7.各種數據類型所占字節
32位平台下:
ps:文中若有不當之處,請指出!