Kotlin基礎學習之位運算
什么是位運算?
程序中的所有數(shù)在計算機內(nèi)存中都是以二進制的形式儲存的。位運算說穿了,就是直接對整數(shù)在內(nèi)存中的二進制位進行操作。比如,and運算本來是一個邏輯運算符,但整數(shù)與整數(shù)之間也可以進行and運算。舉個例子,6的二進制是110,11的二進制是1011,那么6 and 11的結(jié)果就是2,它是二進制對應位進行邏輯運算的結(jié)果(0表示False,1表示True,空位都當0處理):
110 AND 1011 ———- 0010 –> 2
由于位運算直接對內(nèi)存數(shù)據(jù)進行操作,不需要轉(zhuǎn)成十進制,因此處理速度非常快。當然有人會說,這個快了有什么用,計算6 and 11沒有什么實際意義啊。這一系列的文章就將告訴你,位運算到底可以干什么,有些什么經(jīng)典應用,以及如何用位運算優(yōu)化你的程序。
引言
這個還真是基礎中的基礎,如果你跟我一樣之前沒有好好學習過Java基礎語法,這塊對你來說應該是一個懵逼點吧。不談底層什么的,單單從android編程來看,我們在加密算法還有網(wǎng)絡包處理等業(yè)務上使用位運算的頻率還是很高的,更別提Intent中的那些種類繁多的Flag了,因此學好這方面的基礎知識還是很重要的
本系列的例子使用的是Kotlin的語法,跟Java相比還是有所區(qū)別,請對照參考
無符號和有符號
在計算機中,可以區(qū)分正負的類型稱為有符號類型,沒有正負類型的稱為無符號類型。
一個字節(jié)為8位,從0開始算,那它的最高位就是第7位。同樣2個字節(jié)最高位為第15位,4個字節(jié)最高位為第31位。不同長度的類型,最高位不同,但是都是最左邊的那個。
無符號數(shù)中,所有的位都用于直接表示該值的大小;有符號數(shù)中最高位用于表示正負,0表示正數(shù),1表示負數(shù)。因此同樣一個字節(jié),無符號數(shù)最大值為255,有符號數(shù)最大值為127。有符號數(shù)最大值計算完全跟無符號數(shù)一樣,但是在負數(shù)范圍內(nèi)就不能用剛才那種計算方式了,在計算機中,負數(shù)除了最高位為1以外,還采用補碼的形式,所以在計算中要對補碼進行還原
值得注意:的是JAVA的原始類型里沒有無符號整型,如果需要轉(zhuǎn)成無符號類型,可以用ushr
原碼、補碼、反碼
這個是高中就教過的知識,這里就不再做介紹
提醒一下,負數(shù)都是用補碼參與運算的,得到的也是補碼,需要減1取反獲得原碼。
位運算符
位運算主要在直接操控二進制數(shù)時進行使用,可以達到節(jié)約內(nèi)存,使你的程序運行速度更快
Java定義了位運算符,可應用在整形(int)、長整型(long)、短整型(short)以及字符型(byte)等類型上。位運算符作用在所有的位上,并按位進行運算。Kotlin與之略有不同,它并沒有提供特殊的操作符,只提供了中綴形式的表示方法,并且Kotlin只可用在Int和Long類型上,這點千萬要記住
咱們來看一個例子
val a1 = 60 val b1 = 13 var c1 = -5 // 與 println(a1 and b1) // 或 println(a1 or b1) // 異或 println(a1 xor b1) // 按位取反 println(a1.inv()) // 左移 println(a1.shl(1)) // 右移 println(a1.shr(1)) // 無符號右移 println(a1.ushr(1))
先看看結(jié)果,然后我們再一個個的分析
12 61 49 -61 120 30 30
我們知道,Java中的Int是4個字節(jié)32位的,那么a1與b1、c1轉(zhuǎn)換成二進制就應該是
val a1 = 60 // 0000 0000 0000 0000 0000 0000 0011 1100 val b1 = 13 // 0000 0000 0000 0000 0000 0000 0000 1101 var c1 = -5 // 1111 1111 1111 1111 1111 1111 1111 1011
隨后就是7種中綴表達式的計算規(guī)則
and 如果對應位都是1,則結(jié)果為1,否則為0
or 如果對應位都是0,則結(jié)果為0,否則為1
xor 如果對應位值相同,則結(jié)果為0,否則為1
inv 按位翻轉(zhuǎn)操作數(shù)的每一位,即0變成1,1變成0
shl 按位左移指定的位數(shù),相當于乘以2的N次方。移掉的省略,右邊缺失的位,用0補齊
shr 按位右移指定的位數(shù),相當于除以2的N次方,移掉的省略,左邊缺失的位,如果是正數(shù)則補0,若為負數(shù),可能補0或補1,這取決于所用的計算機系統(tǒng)
ushr 按位右移指定的位數(shù),移掉的省略,左邊缺失的位,用0補齊
所以我們來看看按位計算的結(jié)果(省略掉高位重復的0或者1,只看低八位)
通過上述規(guī)則,我們就能明白計算結(jié)果是怎樣得到的
“與”的結(jié)果就是00001100,即為12 “或”的結(jié)果就是00111101,即為61 “異或”的結(jié)果就是00110001,即為49 “非a1”的結(jié)果就是11000011,即為-61 “左移a1 1位”的結(jié)果就是01111000,即為120 “右移a1 1位”的結(jié)果就是00011110,即為30 “無符號右移a1 1位”的結(jié)果就是00011110,即為30
有一個很有趣的現(xiàn)象,對于一個Int類型的數(shù)值,無論你執(zhí)行左移還是右移還是無符號右移,只要移動32位,效果跟沒有移動一致。這是因為在JAVA進行移位運算中因為Int是占32位,進行移位的數(shù)就是32的模,所以當數(shù)值移動32位的時候就等于數(shù)值移動0位,也相當于沒有進行移位。同理Long類型的移位,Long占8字節(jié)也就是64位,所以移位的數(shù)是64的模
應用舉例
有一個位運算口訣大家可以記一下:
清零取反要用與,某位置一可用或
若要取反和交換,輕輕松松用異或
判斷Int型變量a是奇數(shù)還是偶數(shù)
a1 and 1 = 0 // 偶數(shù) a1 and 1 = 0 // 奇數(shù)
獲取Int型變量的第K位(注:K從0開始依次由右往左,以下揭同)
a1 shr k and 1
將Int型變量的第K位清0
a1 and ((1 shl k).inv())
將Int型變量的第K位置1
a1 or (1 shl k)
平均值
(a1 and b1)+((a1 xor b1) shr 1)
不用temp交換兩個整數(shù)
a1 = a1 xor b1 b1 = b1 xor a1 a1 = a1 xor b1
獲取絕對值
val temp = c1 shr 31 (c1 + temp) xor temp (c1 xor temp) - temp
獲取相反數(shù)
c1.inv()+1
Int轉(zhuǎn)byte數(shù)組
val bytes = ByteArray(4) bytes[0] = (a1 and 0xFF).toByte() bytes[1] = (a1 shr 8 and 0xFF).toByte() bytes[2] = (a1 shr 16 and 0xFF).toByte() bytes[3] = (a1 shr 24 and 0xFF).toByte()
補零擴展和補符號位擴展
在看這個問題之前,我們先做一個小游戲
val byte1: Byte = -127 println(byte1) println(byte1.toInt()) println(byte1.toInt() and 0xff)
看官請猜猜看呢
還是我來公布答案吧
-127 -127 129
能解釋一下為什么-127變成129嗎?這個就牽扯到補零擴展和補符號位擴展了
之前我們知道,Java是沒有無符號類型的,byte是8個字節(jié)而int是32個字節(jié)。在byte轉(zhuǎn)int的時候,肯定要將8位補到32位。Java中的擴展方式是補符號位擴展,所以-127通過補符號位擴展之后還是-127,即11111111 11111111 11111111 10000001。如果采用補零擴展,相當于11111111 11111111 11111111 10000001 and 11111111,這個值就是00000000 00000000 00000000 10000001,也就是129了
為了加深記憶,我再留一道題給大家思考
println(0x100000000L + 0xcafebabe.toInt()) println(0x100000000L + 0xcafebabeL)
這兩個表達式打印出來也不一樣,現(xiàn)在你應該能明白為什么不一樣了吧
總結(jié)一下
Java中只有有符號數(shù)。當byte擴展到short,int時,因為符號位是0,所以正數(shù)都一樣,無論如何都是補零擴展;但負數(shù)補零擴展和按符號位擴展結(jié)果完全不同。
補符號位擴展,原數(shù)值不變。補零擴展,相當于把有符號數(shù)看成無符號數(shù)。
對于有符號數(shù),默認采用符號位擴展。由小擴展到大時,需要用and 0xff這樣方式來確保是按補零擴展的;而從大向小時,符號位自動無效,所以不用處理。如果是char類型,那么不管它將要被擴展成什么類型,都執(zhí)行補零擴展
參考文章
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
相關文章
Android自定義view實現(xiàn)圓環(huán)效果實例代碼
本文通過實例代碼給大家介紹了Android自定義view實現(xiàn)圓環(huán)效果,代碼簡單易懂,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下2018-07-07Android Handler之消息循環(huán)的深入解析
本篇文章是對Handler消息循環(huán)進行了詳細的分析介紹,需要的朋友參考下2013-05-05Android編程實現(xiàn)讀取工程中的txt文件功能
這篇文章主要介紹了Android編程實現(xiàn)讀取工程中的txt文件功能,結(jié)合實例形式詳細分析了Android讀取txt文件的原理、操作步驟與相關實現(xiàn)技巧,需要的朋友可以參考下2017-02-02Android實現(xiàn)Service下載文件,Notification顯示下載進度的示例
本篇文章主要介紹了Android實現(xiàn)Service下載文件,Notification顯示下載進度,具有一定的參考價值,有興趣的可以了解一下。2017-01-01Flutter開發(fā)技巧ListView去除水波紋方法示例
這篇文章主要為大家介紹了Flutter開發(fā)技巧ListView去除水波紋方法示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-12-12