3 原碼、反碼、補碼 3.1 知識點補充 在電腦內部,所有信息都是用二進位數串的形式表示的。整數通常都有正負之分,電腦中的整數分為無符號的和帶符號的。無符號的整數用來表示0和正整數,即自然數;帶符號的正數可以表示所有的整數。 由於電腦中符號和數字一樣,都必須用二進位數串來表示,因此,正負號也必 ...
3 原碼、反碼、補碼
3.1 知識點補充
在電腦內部,所有信息都是用二進位數串的形式表示的。整數通常都有正負之分,電腦中的整數分為無符號的和帶符號的。無符號的整數用來表示0和正整數,即自然數;帶符號的正數可以表示所有的整數。
由於電腦中符號和數字一樣,都必須用二進位數串來表示,因此,正負號也必須用0、1來表示。通常我們用最高的有效位來 表示數的符號(當用8位來表示一個整數時,第8位即為最高有效位,當用16位來表示一個整數時,第16位即為最高有效位。)0表示正號、1表示負號。
這種正負號數字化(0表示正號、1表示負號)的機內表示形式就稱為機器碼或者機器數,而相應的機器外部用正負號表示的數稱為真值。將一個真值表示成二進位字串的機器數的過程就稱為編碼
。
無符號數沒有原碼、反碼和補碼一說。只有帶符號數才存在不同的編碼方式。帶符號整數有原碼、反碼、補碼等幾種編碼方式。
**原碼即直接將真值轉換為其相應的二進位形式,而反碼和補碼是對原碼進行某種轉換編碼方式。**正整數的原 碼、反碼和補碼都一樣,負數的反碼是對原碼的除符號位外的其他位進行取反後的結果(取反即如果該位為0則變為1,而該位為1則變為0的操作)。而補碼是先求原碼的反碼,然後在反碼的末尾位加1 後得到的結果,即補碼是反碼+1。IBM-PC中帶符號整數都採用補碼形式表示。
註意,只是帶符號的整數採用補碼存儲表示的,浮點數另有其存儲方式。
- 正數的補碼是其本身
- 負數的反碼,符號位不變,其餘的按位取反
- 負數的補碼,反碼加1
對於字長為8位有符號的int,因為最高為符號位,占1位,所以最小為(1111111)2 = (-127)10,最大為(0111111)2 = (127)10;即其原碼範圍為:-127~127
有符號的8位二進位的原值表達範圍為:-127至127,此時共255個數字;然而,8位二進位 的補碼排列共有$A_{2}^{8}$ = 256個,0000 0000 至1111 1111 。
補碼組合 | 範圍 | 個數 |
---|---|---|
0000 0000 - 0111 1111 | 0 ~+127 | 128 |
10000000 | 多餘的一種組合待定 | 1 |
1000 0001 - 1111 1111 | -1~-127 | 127 |
**10000000 **看似要被浪費掉了啊!其實不然,( 100000000 ) 2 = ( 2^7 ) 10 = ( 128 ) 10,這個組合要利用起來,不能太偏離數值意義,表示128,顯得更直觀。
**從大到小,依次減1看一下規律:**十進位 (字長8bit) | 原碼 | 反碼 | 補碼 |
---|---|---|---|
127 | 0111 1111 | 0111 1111 | 0111 1111 |
126 | 0111 1110 | 0111 1110 | 0111 1110 |
…… | …… | …… | …… |
10 | 0000 1010 | 0000 1010 | 0000 1010 |
…… | …… | …… | …… |
2 | 0000 0010 | 0000 0010 | 0000 0010 |
1 | 0000 0001 | 0000 0001 | 0000 0001 |
+0 | 0000 0000 | 0000 0000 | 0000 0000 |
-1 | 1000 0001 | 1111 1110 | 1111 1111 |
-2 | 1000 0010 | 1111 1101 | 1111 1110 |
…… | …… | …… | …… |
-10 | 10001010 | 11110101 | 11110110 |
…… | …… | …… | …… |
-127 | 11111111 | 10000000 | 10000001 |
待定 | 10000000 |
從遞減規律中,發現,**10000000 **表示-128
更合適。
即規定:-128的補碼為 10000000
求10 -10 0 -128 127 的原碼、反碼、補碼
十進位 (字長8bit) | 原碼 | 反碼 | 補碼 |
---|---|---|---|
10 | 00001010 | 00001010 | 00001010 |
-10 | 10001010 | 11110101 | 11110110 |
-1 | 1000 0001 | 1111 1110 | 1111 1111 |
+0 | 00000000 | 00000000 | 00000000 |
-0 | |||
-128 | 無 | 無 | 10000000 |
127 | 01111111 | 01111111 | 01111111 |
-127 | 11111111 | 10000000 | 10000001 |
+0和-0的補碼是一樣的。即 0的補碼只有一種表示,0的補碼是0000 0000,
# 四 輸出結果,解釋為什麼是這樣的char c = 128;
printf("%d\n",c);
printf("%hhd\n",c);
printf("%hd\n",c);
printf("%hu\n",c);
4.1 格式輸出符
格式符號 | 意義 |
---|---|
%a | 浮點數、十六進位數字和p-記數法 (C99) |
%A | 浮點數、十六進位數字和P-記數法 (C99) |
%c | 一個字元 |
%d | 有符號十進位整數 |
%e | 浮點數、e-記數法 |
%E | 浮點數、E-記數法 |
%f | 浮點數,十進位記數法 |
%g | 根據數值不同自動選擇%f或者%e。%e格式在指數小於-4或者大於等於精度時使用 |
%G | 根據數值不同自動選擇%f或者%E。%E格式在指數小於-4或者大於等於精度時使用 |
%i | 有符號十進位整數 (與%d相同) |
%o | 無符號八進位整數 |
%p | 指針(就是指地址) |
%s | 字元串 |
%u | 無符號十進位整數 |
%x | 使用十六進位數字0f 的無符號十六進位整數 |
%X | 使用十六進位數字0F的無符號十六進位整數 |
%% | 列印一個百分號 |
修飾符 | 意義 | 示例 |
---|---|---|
h | 和整數轉換說明符一起使用,表示一個short int 或者 unsigned short int 類型數值。 | "%hd |
hh | 和整數轉換說明符一起使用,表示一個signed char 或者unsigned char類型數值。 | "%hhd" |
j | 和整數轉換說明符一起使用,表示一個intmax_t或uintmax_t值。 | "%jd" |
l | 和整數說明符一起使用,表示一個long int 或者unsigned long int 類型值。 | "%8lu" |
ll | 和整數說明符一起使用,表示一個long long int或 unsigned long long int 類型值 (C99)。 | "%lld" |
L | 和浮點轉換說明符一起使用,表示一個long double值。 | "%8.4Le" |
t | 和整數轉換說明符一起使用,表示一個ptrdiff_t值(與兩個指針之間的差相對應的類型) (C99) | "%td" |
- | 項目是左對齊的,也就是說,會把項目列印在欄位的左側開始處 | "%-20s" |
+ | 有符號的值若為正,則顯示帶加號的符號;若為負,則帶減號的符號。 | "%+3.2" |
(空格) | 有符號的值若為正,則顯示時帶前導空格(但是不顯示符號);若為負,則帶減號符號。+標誌會覆蓋空格標誌。 | "% 3.2" |
# | 使用轉換說明的可選形式。若為%o格式,則以0開始;若為%x和%X格式,則以0x或0X開始,對於所有的浮點形式,#保證了即使不限任何數字,也列印一個小數點字元。對於%g和%G格式,它防止尾隨零被刪除。 | "%#o" |
0 | 對於所有的數字格式,用前導零而不是用空格填充欄位寬度。如果出現-標誌或者指定了精度(對於整數)則忽略該標誌。 | "%010d" |
char c = 128;
此處是將一個int賦值給一個char類型變數,進行隱式類型轉換.int型數值賦給char型變數時,只保留其最低8位,高位部分捨棄。
首先,整型128在一個字長為4個位元組的的原碼為00000000 00000000 00000000 10000000
,當把一個int類型賦值給一個有符號的char類型時,高位被捨棄。實際給c的是10000000
,此時,被系統認為是一個負數,補碼為10000000
,結合上面的分析,其值就是**-128**。
4.4 格式化輸出
char c = 128;
4.4.1 結論
先給出通過這次作業得出的一個不完全歸納法結論吧,也是我做出的解釋。最後會給出原碼驗證
1、正數的原碼的反碼、補碼是其本身,擴展時,高位補0;
2、負數擴展為有符號的高位補1,無符號的高位補0。
2、負數擴展時,高位補 1。格式化輸出無符號的數據時,機器碼即為原碼;格式化輸出有符號數據時,要先求其原碼,然後求得真值。
c 的機器碼為10000000 。
4.4.2 輸出32位有符號int:
printf("%d\n",c);//預設的 int ,32位
將1000 0000 轉為有符號的32位機器碼:1111 1111 1111 1111 1111 1111 1000 0000
反碼:1111 1111 1111 1111 1111 1111 0111 1111
原碼:1000 0000 0000 0000 0000 0000 1000 0000
有符號,其值為:-128.
4.4.3 輸出8位有符號signed char:
printf("%hhd\n",c);//signed char, 8位
8位機器碼:1000 0000,
此機器碼沒有反碼和源碼,機器碼即為真值:-128.
4.4.4 輸出16位有符號 short int
printf("%hd\n",c);//short int,16 位
將1000 0000 轉為有符號的16位機器碼:1111 1111 1000 0000
反碼:1111 1111 0111 1111 原碼:1000 0000 1000 0000
有符號,其值為:-128.
4.4.5 輸出16位無符號 short int
printf("%hu\n",c);
將1000 0000 轉為16位機器碼:1111 1111 1000 0000
格式化輸出無符號十進位數據,此碼即為原碼。
原碼:1111 1111 1000 0000
做無符號運算,其值:65408
4.5 源碼
#include <iostream>
#include <bitset>
int main() {
using namespace std;
cout << "Hello Biter !" << endl;
char c = 128;
cout << "---------------- -" << int(c) << "----------------------- " << endl;
cout << "char 型 機器碼 = " << bitset<sizeof(char) * 8>(c) << endl;
cout << "int 型 機器碼 = " << bitset<sizeof(int) * 8>(c) << endl;
cout << "signed char 型 機器碼 = " << bitset<sizeof(signed char) * 8>(c) << endl;
cout << "short int 型 機器碼 = " << bitset<sizeof(short int) * 8>(c) << endl;
cout << "unsigned short int 型 機器碼 = " << bitset<sizeof(unsigned short int) * 8>(c) << endl;
cout << "char 二進位形式為 = " << bitset<sizeof(char) * 8>(c) << endl;
cout << "-------------------------------------------- " << endl;
// printf("將 char 直接 輸出 128 超範圍了 = %c\n", c);
// cout << "-------------------------------------------- " << endl;
printf("有符號的 int 輸出 = %d\n", c);
cout << "-------------------------------------------- " << endl;
printf("有符號的 signed char 輸出 = %hhd\n", c);//1000,0000
cout << "-------------------------------------------- " << endl;
printf("有符號的 short int 輸出= %hd\n", c);//0000,0000 1000,0000
cout << "-------------------------------------------- " << endl;
printf("無符號的 unsigned short int 輸出= %hu\n", c);// 1111,1111 1000,0000
cout << "-------------------------------------------- " << endl;
return 0;
}