從java toBinaryString() 看計算機數值存儲方式(原碼、反碼、補碼)


一、toBinaryString 方法及其含義

1.1 方法說明

該方法位於java.lang.Integer類中
方法簽名:public static String toBinaryString(int i)
含義:返回參數數值的補碼形式,正數則忽略前面的0。(官方注釋:返回表示傳入參數的一個無符號(這里無符號大概只是指前面沒有+-號,但還是有符號位) 的二進制字符串。如果參數為負數x,返回值為 2^32 + x 【就是它的補碼】)

1.2 使用示例

    System.out.println(Integer.toBinaryString(3)); // 輸出:11
    System.out.println(Integer.toBinaryString(-248)); // 輸出:11111111111111111111111100001000

二、計算機數值存儲原理

2.1 相關概念

計算機中,包括如Java在內的各語言均是用補碼來存儲數值的

  • 原碼:最高位表示符號位 "1"為負、"0"為正,其他位為正常二進制形式。
  • 反碼:正數的反碼即原碼本身;負數則在原碼的基礎上,除符號位之外的其他各位置反。
  • 補碼:正數的補碼即原碼本身;負數則在反碼基礎上+1。

例:
正數:3 = 0000 0011[原 | 反 | 補]
負數:-3 = 1000 0011[原] = 1111 1100[反] = 1111 1101[補]

注:

  1. 以上規則只是方便我們人來進行推算,而在實際情況中存在不適用的情況,如:1000 0000[補] = -128。關於這點,后面會解釋到。

2.2 為什么會引入補碼

通過引入補碼,CPU只需單純的加法器即可完成加減運算,節約成本。

2.3 背景知識:模系統中的運算及理解

1) 一個數加上模,等於它自身

在任意模系統中,其模為:數所能表示的最大值+1。具體到對於n位二進制,其模則為2^n。
注:但對於一般的加減運算來說,是不需要回到自身的(顯然的事實,但之前莫名其妙老想着回自身),直接加一個數就行了,只是當加數>=模時,會超過模(溢出高位舍去)然后到達一個值。

示例及理解證明

  1. 鍾表上(把12換作0),所能表示的最大數為11,因此其模就為11+1=12。
    根據常識,顯然從4點轉動12小時,又會回到4點本身,即 4 + 12 = 4
  2. 對於2 bit系統,其模為 11b + 1 = 2^2 = 4。
    例:01b + (1)00b = 01b【此處(1)00b表示4,因為高位溢出舍去,顯然還是等於它自身】

2) 什么是同余數?

對於同一個除數的余數相等,則這些數稱為同余數。具體的對於數a、b、c,若有a%c == b%c,則稱a、b彼此是關於c的同余數。

3) 在模系統中,同余數有什么特性?

在模系統中,加上數a可等價為加上它的同余數b。
示例及原因
  在模 n = 12 的系統中,假設有數 a = -4,構造它的同余數 "k*n + a", k=0,1,2...【公式含義:任意倍數的模n加上a,因此顯然是a關於n的同余數(因為倍數k都作為除法的結果上去了,余數始終為 a )】比如 b = 1*12 + (-4) = 8
根據同余數的構造原理顯然可知,在模系統中,加上數a等價於加上同余數b。
【證明步驟:】
c + b = c + k*n + a
因為上述背景知識1),所以 c + k*n = c
所以 c + b = c + a

2.4 將減法轉換為加法

  • 思路1:基於同余數的特性,在模n的系統中,對於減法 a - b,可進行以下轉化:a - b => a + (-b),令 c = n + (-b)【由同余數構造可知,顯然c是(-b)的最小正同余數】,因此 a + (-b) => a + c,而 (-b)的補碼正好就等於c,也就是最小正同余數,所以 a - b => a + (-b)補。
  • 思路2:基於模原理,減法則能換成加法來做:令 c = (n-a) + (a-b) = n-b【 n-a 為了使a+c超過模重置為0,(a-b) 移到具體位置】,而 n-b 顯然是(-b)的同余數【根據同余數構造可知】,因此 a + (-b) = a + c,而 (-b) 的補碼正好等於c。因此 a + b = a + (-b)補。

因此,CPU只需加法器也能完成減法運算。
注:至於為什么(-b)的補碼正好就是最小正同余數c,暫未深挖。就是剛好是。

2.5 補碼系統中,符號位也要參與運算

使用補碼的系統中首位(即符號位)也要參與正常運算,只是當溢出時實際符號可能會倒置,未溢出時運算均是正確的。
(正確性證明,可參考其他博文https://blog.csdn.net/woodpeck/article/details/77747265
比如在8bit系統中:127 + 1 = [0111 1111]補 + [0000 0001]補 = [1000 0000]補 = -128

2.6 為什么定義 1000 0000[補] = -128

網上很多只是說為了不浪費而簡單定義,但光簡單定義肯定不行的,肯定還需要符合運算規律。
其實計算機對補碼的存儲和解釋,不一定非要經過源碼這一環,那是對人的一種換算方式,1000 0000[補] = -128 是符合計算機運算規律的。
比如:-128 + 1 = -127
[1000 0000]補 + [0000 0001]補 = [1000 0001]補 = [1111 1111]原 = -127

不只是1000 0000[補]=-128,在 n bit補碼系統中,對於首位為1其他位為0的數,其值為 -2^(n-1)

部分參考:
https://blog.csdn.net/woodpeck/article/details/77747181


免責聲明!

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



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