【版權(quán)聲明】未經(jīng)博主同意,謝絕轉(zhuǎn)載?。ㄕ堊鹬卦瓌?chuàng),博主保留追究權(quán))
https://www.cnblogs.com/cnb-yuchen/p/17963363
出自【進(jìn)步*于辰的博客】
參考筆記一,P3.13、P5.1;筆記三,P43.1/3、P44.1。
注:我暫且沒有整理關(guān)于二進(jìn)制、原碼、補(bǔ)碼和反碼等概念的理論,本文中的闡述都基于我對相應(yīng)概念的理解,推薦兩篇博文(轉(zhuǎn)發(fā)):
- 關(guān)于對【8位二進(jìn)制的整數(shù)表示范圍、常見位運(yùn)算符和進(jìn)制間轉(zhuǎn)換】的理解與簡述;
- 原碼、反碼、補(bǔ)碼知識詳細(xì)講解(此作者是我找到的講的最細(xì)最明白的一個(gè))。
這兩篇文章都是對我的啟發(fā)之作,一些概念(如:機(jī)器數(shù))也出自于此,建議大家先去瀏覽這兩篇博文,這樣會更便于閱讀本文。
-
1、二進(jìn)制相關(guān)概念
- 1.1 原碼、反碼
- 1.2 補(bǔ)碼
- 2、八位二進(jìn)制的表示范圍
-
3、常見位運(yùn)算符
- 3.1 取反
~
- 3.2 左移
<<
- 3.3 有符號右移
>>
- 3.4 無符號右移
>>>
- 3.5 按位與
&
- 3.6 按位或
|
- 3.7 按位異或
^
- 3.8 擴(kuò)展
- 3.1 取反
-
4、位運(yùn)算運(yùn)用示例
- 4.1 左移與算術(shù)右移的巧妙運(yùn)用
- 4.2 實(shí)現(xiàn)字符大小寫轉(zhuǎn)換
- 4.3 生成 IP 地址
- 5、關(guān)于進(jìn)制間轉(zhuǎn)換
- 最后
1、二進(jìn)制相關(guān)概念
在研究8位二進(jìn)制的表示范圍之前,需要先了解原碼、反碼和補(bǔ)碼這三個(gè)概念。
1.1 原碼、反碼
“原碼”指符號位加上“真值"絕對值的“機(jī)器數(shù)”。
反碼定義:(摘錄自第一篇啟發(fā)之作)
正數(shù)的反碼與其原碼相同;負(fù)數(shù)的反碼是對其原碼逐位取反,但符號位除外。
1.2 補(bǔ)碼
先說說補(bǔ)碼定義:
正數(shù)的補(bǔ)碼是其本身的原碼;負(fù)數(shù)的補(bǔ)碼是其反碼
+1
后的結(jié)果。
補(bǔ)碼是做什么的?(摘錄自第一篇啟發(fā)之作)
引入補(bǔ)碼是為了解決計(jì)算機(jī)中數(shù)的表示和數(shù)的運(yùn)算問題,使用補(bǔ)碼,可以將符號位和數(shù)值域統(tǒng)一處理,即引用了模運(yùn)算在數(shù)理上對符號位的自動(dòng)處理,利用模的自動(dòng)丟棄實(shí)現(xiàn)了符號位的自然處理,僅僅通過編碼的改變就可以在不更改機(jī)器物理架構(gòu)的基礎(chǔ)上完成預(yù)期的要求。
我用幾個(gè)示例簡單說明一下我這段闡述的理解。
示例1:1 + (-2) = -1
看下述示例。
1 + (-2) = 0000 0001 + 1000 0010 = 1000 0011,得:-3
結(jié)果顯然不對,是因?yàn)闆]有考慮到符號位,符號位不能直接參與運(yùn)算。
那若是考慮符號位應(yīng)如何?看示例。
由于-2的絕對值大于1,故:
1 + (-2) = -(2 - 1) = 1(000 0010 - 000 0001) = 1000 0001,得:-1
這顯然增加了硬件的開銷和復(fù)雜性。(本人對硬件了解有限,所以暫且不知此結(jié)論的出處)
若引入補(bǔ)碼:
求-2的補(bǔ)碼:-2的原碼1000 0010 → 反碼1111 1101 → 補(bǔ)碼1111 1110
求1的補(bǔ)碼:1是正數(shù),故就是其原碼0000 0001
計(jì)算:1 + (-2) = 0000 0001 + 1111 1110 = 1111 1111
由于都是補(bǔ)碼,所以1111 1111也是補(bǔ)碼,
1111 1111減1 → 反碼1111 1110 → 原碼1000 0001,真值為:-1
從此示例可看出,補(bǔ)碼將符號位和數(shù)值域統(tǒng)一處理。換言之,不需要考慮正負(fù)大小情況。
示例2:
1、1 + 256 = 1
。256
是8位無符號二進(jìn)制的模,其機(jī)器數(shù)為1 0000 0000
。這是如何得出的?
8位無符號二進(jìn)制的最大機(jī)器數(shù)是1111 1111
,進(jìn)行+1
,得1 0000 0000
,但只有8位,最高位1
自然丟失。得00000000
。因此,1 + 256 = 1 + 0 = 1
。
這就是上文所述的“模的自動(dòng)丟棄實(shí)現(xiàn)了符號位的自然處理”。
2、1 + 128 = 1
。128
是8位有符號二進(jìn)制的模,其機(jī)器數(shù)為1000 0000
(理論上)。這又是怎么個(gè)說法?
先透露:8位有符號二進(jìn)制無法表示128
。
無妨,可暫且將其視為無符號二進(jìn)制進(jìn)行計(jì)算。128 = 127 + 1
,127
的補(bǔ)碼是0111 1111
,進(jìn)行+1
,得1000 0000
。
那么,1 + 128 = 0000 0001 + 1000 0000
,得1000 0001
。不過,我們只是將其視為無符號,實(shí)際有符號,故符號位無效,不進(jìn)行計(jì)算。因此,結(jié)果為0000 0001
,真值為1
。
注意:在計(jì)算機(jī)底層,數(shù)都是以補(bǔ)碼的形式進(jìn)行表示。
擴(kuò)展一點(diǎn):
在查閱有關(guān)補(bǔ)碼的資料時(shí),我注意到一些文章中闡述的負(fù)數(shù)補(bǔ)碼的另一種計(jì)算方法:
絕對值原碼 → 取反 → +1
綜本文所述,補(bǔ)碼的計(jì)算方法:
原碼 → 反碼 → +1
另一種方法可行嗎?
經(jīng)過觀察,結(jié)論是:當(dāng)然可行。
負(fù)數(shù)絕對值原碼與負(fù)數(shù)原碼的不同唯有符號位,如:
1 = 0000 0001
、-1 = 1000 0001
,故負(fù)數(shù)絕對值原碼取反與負(fù)數(shù)反碼相同。
因此,兩種方法都可行,大家覺得怎么方便怎么來。
2、八位二進(jìn)制的表示范圍
1、無符號二進(jìn)制。
既然“無符號”,則無正負(fù)之分,都表示正數(shù)。
-
0000 0000
→ 0; -
1111 1111
→ 27 + 26 + ... + 21 + 20 = 255。
因此,8位無符號二進(jìn)制的表示范圍是0 ~ 255
。
2、有符號二進(jìn)制。
關(guān)于8位有符號二進(jìn)制的表示范圍,其中細(xì)節(jié)比較復(fù)雜,暫不討論,我暫且簡述,詳述可查閱第一篇啟發(fā)博文。
分析:
8位二進(jìn)制的范圍是0000 0000 ~ 1111 1111
。
- 正區(qū)間
0000 0000 ~ 0111 1111
,即0 ~ 127
。 - 負(fù)區(qū)間
1000 0000 ~ 1111 1111
,首先,1111 1111 ~ 1000 0001
是-127 ~ -1
,那1000 0000
的真值是多少?
1000 0000 = 1000 0001 - 1
(無視符號位,這樣等式才成立),可實(shí)際上都表示負(fù)數(shù),則是+1
。1000 0001
是-1
,故1000 0000
是0
。
可0
明明是0000 0000
?那么,可視為+0
,則1000 0000
是-0
。
從上文【示例2】可知,8位有符號二進(jìn)制的模是128
。因此,-0
等同于-128
。所以,1000 0000
的真值為-128
。
補(bǔ)充說明:
上述推導(dǎo)基于原碼,當(dāng)然也可以使用補(bǔ)碼,只是原碼更易于理解。
如下:1000 0000 = 1000 0001 - 0000 0001
(由于都是補(bǔ)碼,故等式也成立),1000 0001
是-127
,則1000 0000
是-128
。
為什么是補(bǔ)碼,等式就成立?
“引入補(bǔ)碼”的目的之一是為了解決數(shù)的運(yùn)算問題,比如:-1 + 2
,用原碼運(yùn)算時(shí)就要考慮絕對值的大小問題。引入補(bǔ)碼后就無需考慮,故可以說“負(fù)數(shù)的補(bǔ)碼是正數(shù)”(當(dāng)然負(fù)數(shù)的補(bǔ)碼還是負(fù)數(shù),只是視為正數(shù))。
因此,8位有符號二進(jìn)制的表示范圍是-128 ~ 127
。
3、常見位運(yùn)算符
~ | << | >> | >>> | & | | |
^ |
---|---|---|---|---|---|---|
取反 | 左移 | 有符號右移 | 無符號右移 | 按位與 | 按位或 | 按位異或 |
先說明:
- 下文中的“高位”指第一位,“低位”指最后一位。(左 → 右)
- 二進(jìn)制在計(jì)算機(jī)中都是以補(bǔ)碼的形式進(jìn)行表示。因此,以下示例都要遵循補(bǔ)碼定義。
3.1 取反 ~
運(yùn)算規(guī)則:逐位反轉(zhuǎn),即:0 → 1, 1 → 0
。
示例:計(jì)算~23
。
23的補(bǔ)碼:0001 0111
~23 = 1110 1000
1110 1000 → 反碼1110 0111 → 原碼1001 1000,真值為-24
擴(kuò)展一點(diǎn):
在運(yùn)算規(guī)則上,取反~
與反碼相同。不過,~
不考慮符號位,與反碼定義不同。
3.2 左移 <<
運(yùn)算規(guī)則:去高位,補(bǔ)低位,補(bǔ)0
。
示例:
1、計(jì)算23 << 1
。
23的補(bǔ)碼:0001 0111
23 << 1 = 0010 1110,真值為46
2、計(jì)算 -23 << 1
。
計(jì)算-23的補(bǔ)碼:-23原碼1001 0111 → 反碼1110 1000 → 1110 1001
-23 << 1 = 1101 0010
11010010 → 反碼1101 0001 → 原碼1010 1110,真值為-46
3.3 有符號右移 >>
也稱之為“算術(shù)右移運(yùn)算符”。
運(yùn)算規(guī)則:去低位,補(bǔ)高位。若是正數(shù),補(bǔ)0
;否則,補(bǔ)1
(一直補(bǔ))。
示例:
1、計(jì)算23 >> 1
。
23的補(bǔ)碼:0001 0111
23 >> 1 = 0000 1011,真值為11
2、計(jì)算 -23 >> 1
。
-23的補(bǔ)碼:1110 1001
-23 >> 1 = 1111 0100
1111 0100 → 反碼1111 0011 → 原碼1000 1100,真值為-12
3.4 無符號右移 >>>
也稱之為“邏輯右移操作符”。
運(yùn)算規(guī)則:去低位,補(bǔ)高位,無論正負(fù),都補(bǔ)0
。
示例:
1、計(jì)算23 >>> 1
。
23的補(bǔ)碼:0001 0111
23 >>> 1 = 0000 1011,真值為11
2、計(jì)算-23 >>> 1
。
-23的補(bǔ)碼:11111111 11111111 11111111 11101001
-23 >>> 1 = 01111111 11111111 11111111 11110100,
真值為Integer.MAX_VALUE - 11 = 2147483636
從上文可知,負(fù)數(shù)補(bǔ)碼的第一個(gè)1位前都是1
。
3.5 按位與 &
運(yùn)算規(guī)則;當(dāng)對應(yīng)二進(jìn)制位同為1
時(shí),得1
。(不考慮正負(fù))
示例:
1、計(jì)算7 & 23
。
7的補(bǔ)碼:0000 0111
23的補(bǔ)碼:0001 0111
7 & 2 = 0000 0111,真值為7
2、計(jì)算7 & -23
。
7的補(bǔ)碼:0000 0111
-23的補(bǔ)碼:1110 1001
7 & -23 = 0000 0001,真值為1
3.6 按位或 |
運(yùn)算規(guī)則:當(dāng)對應(yīng)二進(jìn)制位有一個(gè)為1
時(shí),得1
。(不考慮正負(fù))
示例:
1、計(jì)算7 | 23
。
7的補(bǔ)碼:0000 0111
23的補(bǔ)碼:0001 0111
7 | 23 = 0001 0111,真值為23
2、計(jì)算7 | -23
。
7的補(bǔ)碼:0000 0111
-23的補(bǔ)碼:1110 1001
7 | -23 = 1110 1111,真值為-7
3.7 按位異或 ^
運(yùn)算規(guī)則:當(dāng)對應(yīng)二進(jìn)制位相同時(shí)得0
,否則得1
、(不考慮正負(fù))
示例:
1、計(jì)算7 ^ 23
。
7的補(bǔ)碼:0000 0111
23的補(bǔ)碼:0001 0111
7 ^ 23 = 0001 0000,真值為16
2、計(jì)算7 ^ -23
。
7的補(bǔ)碼:0000 0111
-23的補(bǔ)碼:1110 1001
7 ^ -23 = 1110 1110,真值為-18
3.8 擴(kuò)展
我在看Java-API源碼時(shí),遇到一些奇怪的位運(yùn)算符,如:
<<=
、|=
,這些位運(yùn)算符平日很少見,一查才知道,其實(shí)就是位運(yùn)算后賦值,類似+=
、*=
。
示例見Integer類的第4.12項(xiàng),其底層運(yùn)用位運(yùn)算|=
獲取最高位的 1 位的位置。
4、位運(yùn)算運(yùn)用示例
4.1 左移與算術(shù)右移的巧妙運(yùn)用
先說結(jié)論:
<<
的結(jié)果是原來的2倍,>>
的結(jié)果是原來的1/2(取整,即若是奇數(shù),先-1
,再取1/2
)。
示例說明:
1、23 << 1 = 46
。
計(jì)算:
23 = 16 + 4 + 2 + 1 = 24 + 22 + 21 + 20 → 0001 0111
;23 << 1 = 0010 1110
→ 25 + 23 + 22 + 21 = 2*(24 + 22 + 21 + 20)。
——————
每1位都左移1位,變成原來的2倍,故總和也是原來的2倍。
2、23 >> 1 = 11
。
計(jì)算:23 >> 1 = 0000 1011
→ 23 + 21 + 20 = (24 + 22 + 21 + 20)/2。
——————
當(dāng)然,這個(gè)等式是不成立的,因?yàn)榈忍栍疫叾嗔藗€(gè) 20,即多了1
。不過,大家肯定已經(jīng)看出來了我這么寫的用意。23
是奇數(shù),它的二進(jìn)制是0001 0111
,最低位是1。右移1位,這個(gè) 1
/20 就沒了,等式成立;而其他位都變?yōu)樵瓉淼?code>1/2。因此,總和也變成原來的1/2
。
3、-11 >> 1 = -6
。
計(jì)算-11的補(bǔ)碼:-11原碼1000 1011 → 反碼1111 0100 → 補(bǔ)碼1111 0101
-11 >> 1 = 1111 1010
1111 1010 → 反碼1111 1001 → 原碼1000 0110,真值為-6
-11
是奇數(shù),同樣先-1
,再取1/2
。
4、-6 << 1 = -12
。
計(jì)算-6的補(bǔ)碼:-6原碼1000 0110 → 反碼1111 1001 → 補(bǔ)碼1111 1010
-6 << 1 = 1111 0100
1111 0100 → 反碼1111 0011 → 原碼1000 1100,真值為-12
之所以計(jì)算得這么詳細(xì),是為了方便大家觀察>>/<<
兩種位運(yùn)算的規(guī)律,即結(jié)果相反。
補(bǔ)充說明:
其實(shí)負(fù)數(shù)的位運(yùn)算不必計(jì)算其補(bǔ)碼(純屬個(gè)人習(xí)慣)。
示例:
-10的原碼:1000 1010
-10 >> 1 = 1000 0101,真值為-5 // 注意:這里右移補(bǔ)的是0
-5 << 1 = 1000 1010,真值為-10
哈哈。。。其實(shí)此技巧也不絕對。通過此方法進(jìn)行計(jì)算,比如:-11 >> 1 = -5
、-15 >> 1 = -7
,兩個(gè)結(jié)果顯然不對。當(dāng)然,我還做了其他測試,負(fù)奇數(shù)和負(fù)偶數(shù)都有,結(jié)果有正確的也有錯(cuò)誤的。
由于這只是我發(fā)現(xiàn)的一個(gè)規(guī)律,并沒有理論支撐,所以未列舉出來進(jìn)行說明。不過,似乎負(fù)偶數(shù)使用此技巧運(yùn)算的結(jié)果是對的(也算是一個(gè)規(guī)律吧。。。)。
因此,大家對這個(gè)技巧有個(gè)印象就行,實(shí)際計(jì)算還是要嚴(yán)謹(jǐn)。
結(jié)語:
為什么說上文這個(gè)性質(zhì)巧妙?
因?yàn)槲野l(fā)現(xiàn)在很多源碼中都采用這種方法進(jìn)行數(shù)值翻倍或取半,特別是一些包裝類(java.lang.*
)或工具類(java.util.*
)。
例如:
這是java.util.ArrayList<E>
類的擴(kuò)容方法,很經(jīng)典的例子,方法詳情見源博文的第5.6項(xiàng)。
4.2 實(shí)現(xiàn)字符大小寫轉(zhuǎn)換
char 類型對應(yīng)ASCLL碼,對字符進(jìn)行
-/+ 32
運(yùn)算即可實(shí)現(xiàn)大小寫轉(zhuǎn)換。
在查閱關(guān)于位運(yùn)算的資料時(shí),我發(fā)現(xiàn)通過位運(yùn)算也可以實(shí)現(xiàn)字符大小寫轉(zhuǎn)換。由于位運(yùn)算的對象是二進(jìn)制,故效率優(yōu)于算術(shù)運(yùn)算。好奇測試一下發(fā)現(xiàn),如果都運(yùn)算一億次,時(shí)間差在幾十甚至幾微秒之間,實(shí)際差距微乎其微。因此,我將此方法記錄下來的主要目的是為了“擴(kuò)展思維”。
看下述示例。
1、'A' ^= 32 → 'a'
。
計(jì)算字符'A'的補(bǔ)碼:'A' = 65 = 0100 0001
`'A' ^ 32 = 0100 0001 ^ 0010 0000 = 0110 0001,真值為97
2、'a' ^= 32 → 'A'
。
'a'的補(bǔ)碼:0110 0001
'a' ^ 32 = 0110 0001 ^ 0010 0000 = 0100 0001,真值為65
3、'A' |= 32 → 'a'
。
'A' | 32 = 0100 0001 | 0010 0000 = 0110 0001,真值為97
4、'a' &= -33 → 'A'
。
計(jì)算-33的補(bǔ)碼:-33的原碼1010 0001 → 反碼1101 1110 → 補(bǔ)碼1101 1111
'a' & -33 = 0110 0001 & 1101 1111 = 0100 0001,真值為65
4.3 生成 IP 地址
大家看一個(gè)例子就明白了,看這里 → Inet4Address類的第2.2項(xiàng),這里就不再贅述。
5、關(guān)于進(jìn)制間轉(zhuǎn)換
- 八進(jìn)制以
0
(零)開頭,十六進(jìn)制以0x
(零)開頭; - 八進(jìn)制以
0 ~ 7
這8個(gè)數(shù)表示,十六進(jìn)制以0 ~ 9 a ~ f
這10個(gè)數(shù)和5個(gè)字母表示; - 十六進(jìn)制中的
a
與A
相同; - 其他進(jìn)制
<- ->
十進(jìn)制、二進(jìn)制<- ->
十進(jìn)制的運(yùn)算方式,兩者相同;(<- ->
表示相互轉(zhuǎn)換) - 十六進(jìn)制
->
二進(jìn)制的方法:將每一位轉(zhuǎn)換成4
位二進(jìn)制,然后合并(注:4
位二進(jìn)制最大值為1111
,得15
;十六進(jìn)制的最大值為f
,即15
)。
示例:1、0xa
是10
,10
的二進(jìn)制是0000 1010
;2、0x14
是20
,1
的二進(jìn)制是0001
,4
的二進(jìn)制是0100
,合并得0001 0100
,即20
。
最后
本文中的例子為了闡述這七種常見位運(yùn)算符的運(yùn)算步驟、方便大家理解而簡單舉例的,23
與-23
是任意取的數(shù),沒有特別意義。
PS:
單純的位運(yùn)算最大的作用就是幫助我們掌握位運(yùn)算的基礎(chǔ),沒有太大實(shí)用價(jià)值。大家可以偶爾去看看一些源碼中對位運(yùn)算的運(yùn)用,很多真的很巧妙,而且還能查漏補(bǔ)缺。
本文能有如此規(guī)模,得益于我對二進(jìn)制、位運(yùn)算的理解,以及平日解析源碼時(shí)頻繁運(yùn)用位運(yùn)算。文章來源:http://www.zghlxwxcb.cn/news/detail-787612.html
本文完結(jié)。文章來源地址http://www.zghlxwxcb.cn/news/detail-787612.html
到了這里,關(guān)于關(guān)于二進(jìn)制的原碼、補(bǔ)碼和反碼,以及表示范圍、常見位運(yùn)算符和進(jìn)制轉(zhuǎn)換的理解與簡述的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!