補碼與符號位取反


補碼與符號位取反

先來一個 C 語言的小例子:

#include <stdio.h>
#include <stdint.h>

int main(void)
{
    int16_t n = -1;
    n &= 0x7FFF;        // 按位與
    printf("%d", n);    // 這里輸出什么?
    return 0;
}

對於16位的整數 n ,按位與運行將最高位設置為0(符號位),得到的結果卻不是 1 ,結果是 32767 。

原因在於有符號整數的實現方式。

有符號整數,最容易想到的方式是在最高位加一個符號位,0表示整數,1表示負數,其它位不變(保留原始值),也即是原碼方式。但這個方式有一個問題,存在兩個0,正0和負0,在計算時需要先判斷符號位,然后才能決定用加法還是減法,機器計算不便。

另外一個方法是負數全部按位取反,也就是反碼方式。這個運算就相對簡單了,進行加法時,按位計算,0和0為0,0為1為1,1和1為0並產生進位,最高位有進位時,結果要加1,減法可處理為其負數的加法。但還是有點問題,還存在兩個0,正0和負0。

問題是出現負數上,那么把負數的反碼 + 1 ,不就把負0去掉了嗎?還真的是這樣,而同時負數比整數能多表示一個數(這是基於同余的)。

嚴格的表達為:

對於位數為 n 的整數,其補碼 [x]補 為:(2^n + x) mod 2^n ,
表示的范圍為 -2^(n-1) <= n < 2^(n-1) ,注意正數最大為 2^(n-1)-1
即:
當 0 <= x < 2^(n-1) 時,
[x]補 = x 的原碼
當 -2^(n-1) <= x < 0 時,
[x]補 = 2^n + x 的原碼。

而經驗上,可看作負數的補碼為其反碼加1(特殊數 -2^(n-1) ) 的反碼 :

x < 0 時:
[x]補 = [x]反 + 1
特殊數 [ -2^(n-1) ] = 100...0

它的加法處理非常簡單,符號位也可以運行,

[x+y]補 = (2^n + x + y) mod 2^n = ((2^n + x) + (2^n + y)) mod 2^n = [x]補 + [y]補
[x-y]補 = (2^n + x - y) mod 2^n = ... = [x]補 + [-y]補

現在回到原來的問題,對於16位 -1 ,其補碼為:

[-1]補 = [-1]反 + 1 = 0xFFFFF

按位與去掉符號位,得到的是 0x7FFF 也就是16位整數最大的正數( 2^15 - 1) 32767。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM