有符號數和無符號數混用的思考


參考文章:

[1] C語言中無符號數和有符號數之間的運算

[2] 負數在計算機中怎樣存儲

[3] 二進制減法


1、負數如何在計算機中存儲?

原碼:10進制轉換成2進制是原碼,只不過正數的原碼是本身符號位爲0,負數的原碼符號位爲1(以下篇幅均以單字節爲例:10進制1的原碼是0000 0001,10進制-1的原碼是1000 0001)。

反碼: 正數的反碼是本身,負數的反碼是負數的原碼0變爲1,1變爲0   (-1的原碼是1000 0001   它的反碼就是 1111 1110,)。(注意負數求反碼時候的符號位不參與變換)。

補碼: 正數的補碼是本身,負數的反碼就是負數的反碼加一 (-1的原碼是1000 0001   它的反碼就是 1111 1110  它的補碼就是 1111 1111)。

總結:正數的原碼,反碼 ,補碼三值合一, 負數的原碼,反碼,補碼不同。

爲什麼要引入反碼、補碼?

總結:計算機中正數和負數都是以補碼的形式存在的,方便做減法(只不過正數的補碼是本身)。

2、無符號數相減爲負數(負數符號位忽略,視爲無符號數處理)

當表達式中存在有符號數和無符號數時,計算結果會視爲無符號數。

unsigned u = 10, u2 = 42;
std:cout << u2 - u << std:endl;
std:cout << u - u2 << std:endl;

輸出結果:32    4294967264

u在計算機中的存儲:00000000 00000000 00000000 00001010(補碼)

u2在計算機中的存儲:00000000 00000000 00000000 00101010(補碼)

u2 - u:

    00000000 00000000 00000000 00101010
-   00000000 00000000 00000000 00001010
    00000000 00000000 00000000 00100000  -> 32

u - u2:

    00000000 00000000 00000000 00001010
-   00000000 00000000 00000000 00101010
    11111111 11111111 11111111 11100000 U  -> 4294967264
    

3、有符號數與無符號數加減運算

當表達式中存在有符號數和無符號數時,計算結果會視爲無符號數。

#include "iostream"

using namespace std;

int main()
{
        int c = -16;
        unsigned int d = 15;
        cout << c + d << endl;
        return 0;
}

輸出結果:4294967295

    11111111 11111111 11111111 11110000 (-16補碼)
+   00000000 00000000 00000000 00001111 (15補碼)
    11111111 11111111 11111111 11111111 (視爲無符號數就是4294967295)

注意:這段代碼如果寫成C形式,輸出需要寫成如下形式,不然%d指的是有符號數。

printf("%u\n",c + d);

4、有符號數與無符號數比較

#include <iostream>

using namespace std;

int main()
{
    int a = -1;
    unsigned int b = 16;
    if(a > b) {
        cout << "a - b = " << a - b << endl;
        cout<<"負數竟然大於正數了!\n";
    }

    return 0;
}

輸出結果:

其實這個很好理解,把 if (a > b) 轉換爲 if(a - b > 0),現在根據3所述,a - b是大於0的。

    11111111 11111111 11111111 11111111 (-1的補碼)
-   00000000 00000000 00000000 00010000 (16的補碼)
    11111111 11111111 11111111 11101111 -> 計算結果視爲無符號數,那就是4294967279

5、char和unsigned char混合運算

#include <iostream>

using namespace std;

int main()
{
    char a = -1;
    unsigned char b = 16;
        cout << "a - b = " << a - b << endl;
    if(a > b) {
        //    cout << "a - b = " << a - b << endl;
        cout<<"負數竟然大於正數了!\n";
    }

    return 0;
}

輸出:

顯然結果與上面不一樣了,WHY?

C語言中比int小的整型(包括short 、unsigned short 、 unsigned char和char)在運算中,考慮到運算效率的問題都要轉換成int然後進行運算。

如果是unsigned的類型轉換成int類型,高位補0.

如果是signed的類型轉換成int類型,如果原來最高位是1則補1,如果是0則補0

char a = -1;
a -> 1111 1111 -> 補成int: 11111111 11111111 11111111 11111111
unsigned char b = 16;
b -> 0001 0000 -> 補成int: 00000000 00000000 00000000 00010000

    11111111 11111111 11111111 11111111
-   00000000 00000000 00000000 00010000
    11111111 11111111 11111111 11101111 <- 補碼 11111111 11111111 11111111 11101110 
                                        <- 原碼 10000000 00000000 00000000 00010001 (int類型-17)

對於unsigned short和short進行運算,和char和unsigned char一樣,都是要先轉換成int,然後再進行運算。

轉換時高位補齊的方法也和unsigned char、char一樣。


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章