1. 程式人生 > >二進制那些事

二進制那些事

二進制

理清字符集和字符編碼關系中介紹到計算機內部由集成電路決定了計算機的信息只能用二進制數處理。本期將介紹二進制那些事。

移位運算

移位運算指的是將二進制數值的各數位進行左右移位的運算。左移空出來的低位要進行補0操作,右移空出來的高位要進行怎樣的操作,我們會在後面說明。
技術分享圖片

我們發現,左移兩位相當於對39乘以4,右移兩位相當於除4,也就是說計算機用移位算法來表示數據的乘除運算

補數

剛才之所有沒有介紹相關右移的內容,是因為用來填充右移後空出來的高位的數值,有 0 和 1 兩種形式。要想區分什麽時候補0什麽時候補1,只要掌握了用二進制數表示負數的方法即可。

二進制數中表示負數時,一般會把最高位作為符號來使用,也就是說,最高位是符號位。正數的符號位用0表示,負數的符號位用1表示。舉個栗子,1的二進制數是0000 0001 ,那麽,-1的二進制數是多少呢?難道是1000 0001,1000 0001+0000 0001 結果不是0,說明這個結果是錯的。為此,在表示負數時就需要使用補數

補數就是用正數表示負數,通過將二進制數的各位數值全部取反,然後再將結果加1得到補數。-1的補數是1111 1111。同理,1111 1110表示的負數是多少呢?這時我們可以利用負負得正這個性質。假設1111 1110是負xx,那麽1111 1110的補數是正xx。1111 1110的補數是0000 0010,因此1111 1110表示-2。

邏輯右移和算術右移

在介紹完補數後,讓我們返回到右移這個話題,右移之後在最高位有補0和補1兩種情況。當二進制數的值表示圖形模式而非數值時,移位後在最高位補0,這是邏輯右移。將二進制數值作為帶符號的數值進行運算時,移位後要在最高位填充前符號位的值( 0 或 1 ),這是算術右移

現在我們來看一個右移的例子。將-8(1111 1000)右移兩位。這時,邏輯右移的情況下結果會是 0011 1110,也就是十進制數62,顯然不是-2,而在算術右移的情況下,結果會變成1111 1110 ,用補數表示就是-2,和真實結果相同。需要註意的是只有在右移時才區分邏輯移位和算術移位。

二進制數表示小數

通過上述介紹,我們對整數的二進制表示方式做了說明。由於計算機內部所有信息都是以二進制數的形式來處理,因此在這一點上,整數和小數並無差別。不過,使用二進制數表示整數和小數的方法卻有很大的不同。

由於二進制數表示的小數的數值範圍是有限的,並不能表示所有的十進制小數。例如:小數點後3位用二進制數表示時的數值範圍為0.000~0.111,但是只能表示有限的十進制小數,如下圖所示。
技術分享圖片
為了加深大家印象,舉一個更加實際的栗子:將0.1累加100次最終結果是10.000002,不是10。

public class TestBinary {
    public static void main(String[] args) {
        float sum=0.0f;
        for (int i = 0; i < 100; i++) {
            sum += 0.1;
        }
        System.out.println(sum);

    }
}

浮點數

現在,我們應該知道僅僅依靠紙面上的二進制數是表示不了全部小數。那麽,計算機實際上是以什麽樣的表現形式來處理小數的呢?

目前,計算機提供了單精度浮點數和雙精度浮點數來表示小數形式。單精度浮點數用32位表示全體小數,而雙精度浮點數用64位表示。它們都是由符號、尾數和指數組成。
技術分享圖片

接下來,讓我們一起看一下如果將0.1用單精度浮點數來表示,累加100次的結果是否是10,可以結果又一次出乎意料,結果是10.000002。

public class TestBinary {
    public static void main(String[] args) {
        float sum=0.0f;
        float step = 0.1f;
        for (int i = 0; i < 100; i++) {
            sum += step;

        }
        System.out.println(sum);

    }
}

如何避免計算機計算出錯

我們知道,無論是用紙面二進制還是浮點數表示小數,都存在計算出錯的可能性,那麽我們該如何避免這種問題呢?

首先是回避策略,即無視這些問題。有時候一些微小的偏差並不會造成什麽問題。其次,把小數轉換成整數來計算。還是以0.1累加100次為例,將0.1擴大10倍後累加100次,最後把結果除以10就可以了。

public class TestBinary {
    public static void main(String[] args) {
        int sum=0;
        int step = 1;
        for (int i = 0; i < 100; i++) {
            sum += step;

        }
        System.out.println(sum/10);

    }
}

二進制那些事