~0 == -1 問題全解

我們先看一個題目

#include<stdio.h>

int main()
{
	unsigned a,b,c,d;   //或者   int a,b,c,d;
	a=0x8;
	b=a>>1;
	c=~(~0<<1);
	d=b&c;
	printf("c is %d\n",c);
	printf("d is %d\n",d);
}

在這裏插入圖片描述
假設計算機是存儲八位

0的存儲是   0b 0000 0000
~按位取反   0b 1111 1111
左移一位    0b 1111 1110     空檔處補0
再按位取反  0b 0000 0001

所以c就等於1

這裏是一步一步推導過來的,你會忽略一個關鍵的過程,就是~0你算出來是
0b 1111 1111,一個很大的負數,這和你以爲的常理違背,我們下面討論。

再次舉例

#include <stdio.h>
int main()
{
    printf("~0 == %d\n", ~0);
}

在這裏插入圖片描述

0的存儲是   0b 0000 0000
~按位取反   0b 1111 1111

這個題目比較乾脆,直接是~0 == -1,有的時候我們不明白,爲什麼0b 1111 1111在內存中代表-1,因爲他無論如何也是一個很大的負數纔對。

可是實際上,負數在內存中是按照補碼的形式存儲的,也就是說0b 1111 1111是一個補碼,那麼它的反碼就是0b 1111 1110,原碼就是0b 1000 0001,也就是-1(注意負數求反碼補碼的時候符號位不變)

所以結論

0b 1111 1111 == -1~00b 1111 1110 == -2~10b 1111 1101 == -3~2

可以把它當做一個公式 ~a == -【a+1】

補充說明

爲什麼整數要在內存中按照補碼儲存。因爲正數的原碼反碼補碼都一樣,所以我們主要討論問什麼負數在內存中按照補碼方式存儲。

採用補碼的原因或好處如下,採用補碼運算具有如下兩個特徵:

1)因爲使用補碼可以將符號位和其他位統一處理,同時,減法也可以按加法來處理,即如果是補碼錶示的數,不管是加減法都直接用加法運算即可實現。

2)兩個用補碼錶示的數相加時,如果最高位(符號位)有進位,則進位被捨棄。

這樣的運算有兩個好處:

1)使符號位能與有效值部分一起參加運算,從而簡化運算規則。從而可以簡化運算器的結構,提高運算速度;(減法運算可以用加法運算表示出來。)

2)加法運算比減法運算更易於實現。使減法運算轉換爲加法運算,進一步簡化計算機中運算器的線路設計。

用帶符號位的原碼進行乘除運算時結果正確,而在加減運算的時候就出現了問題,如下: 假設字長爲8bits

( 1 ) - ( 1 ) = ( 1 ) + ( -1 ) = ( 0 )

(00000001)原 + (10000001)原 = (10000010)原 = ( -2 ) 顯然不正確.。

因爲在兩個整數的加法運算中是沒有問題的,於是就發現問題出現在帶符號位的負數身上,對除符號位外的其餘各位逐位取反就產生了反碼。反碼的取值空間和原碼相同且一一對應。下面是反碼的減法運算:

( 1 ) - ( 1 ) = ( 1 ) + ( -1 ) = ( 0 )

(00000001) 反+ (11111110)反 = (11111111)反 = ( -0 ) 有問題。

( 1 ) - ( 2) = ( 1 ) + ( -2 ) = ( -1 )

(00000001) 反+ (11111101)反 = (11111110)反 = ( -1 ) 正確

問題出現在(+0)和(-0)上,在人們的計算概念中零是沒有正負之分的。

於是就引入了補碼概念。負數的補碼就是對反碼加一,而正數不變,正數的原碼反碼補碼是一樣的。在補碼中用(-128)代替了(-0),所以補碼的表示範圍爲:

(-128~127)共256個。

注意:(-128)沒有相對應的原碼和反碼, (-128) = (10000000) 補碼的加減運算如下:

( 1 ) - ( 1 ) = ( 1 ) + ( -1 ) = ( 0 )

(00000001)補 + (11111111)補 = (00000000)補 = ( 0 ) 正確

( 1 ) - ( 2) = ( 1 ) + ( -2 ) = ( -1 )

(00000001) 補+ (11111110) 補= (11111111)補 = ( -1 ) 正確

採用補碼錶示還有另外一個原因,那就是爲了防止0的機器數有兩個編碼。原碼和反碼錶示的0有兩種形式+0和-0,而我們知道,+0和-0是相同的。這樣,8位的原碼和反碼錶示的整數的範圍就是-127-127(11111111-01111111),而採用補碼錶示的時候,00000000是+0,即0;10000000不再是-0,而是-128,這樣,補碼錶示的數的範圍就是-128~+127了,不但增加了一個數得表示範圍,而且還保證了0編碼的唯一性。

爲什麼正數的反碼,補碼和原碼一樣

這是規定 或者說這是約定,沒有多少道理,你算是算不出來的。

補碼只是爲 負數 想出來的辦法,目的是減法變加法。是減法可以 用 加補碼 的方法實現。補碼 可用反碼加1得來。於是道又有了 負數的反碼。

計算機裏有硬件“加法器”,有了補碼,減法 也可以 用 加法器 做了。計算機 裏運算速度,硬件遠快於軟件。這是弄出反碼,補碼和原碼花樣的原因。

形象說明

引進補碼的作用是爲了讓計算機更方便做減法
比如說,按時間12個小時來算
現在的準確時間是4點
有一個表顯示的是7點
要校準時間,我們可以將時針退7-4=3格,也可以向前撥12-3=9格
計算機做減法就可以轉化成-3=+9
這樣可以簡化計算機的硬件設備去做複雜的減法

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