日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

一文搞定位运算

發(fā)布時(shí)間:2025/3/20 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 一文搞定位运算 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

目錄

一、前言

二、加減乘除運(yùn)算原理

2.1 加法和乘法

2.2 減法和除法

三、位運(yùn)算

3.1 按位與 &?

3.2 按位或 |

3.3 按位異或 ^

3.4 取反 ~

3.5 按位左移<<

3.6 按位右移 >>

3.7 無(wú)符號(hào)按位右移 >>>??

3.8 總結(jié)

四、位運(yùn)算應(yīng)用

4.1 常用位運(yùn)算

4.2 顏色轉(zhuǎn)換

4.3 枚舉

4.4 加密


位運(yùn)算總結(jié):

按位與 &

同1為1

按位或 |

任1為1

按位異或 ^

同1為1

取反 ~

值取反

左移 >>

廢棄左側(cè)指定位數(shù)后左移,空位補(bǔ)0

右移 <<

廢棄右側(cè)指定位數(shù)后右移,空位補(bǔ)0

無(wú)符號(hào)右移 <<<

忽略符號(hào)位的右移

?

一、前言

在現(xiàn)代計(jì)算機(jī)中所有的數(shù)據(jù)都是以二進(jìn)制的形式存儲(chǔ)在設(shè)備中。即0、1兩種狀態(tài),計(jì)算機(jī)對(duì)二進(jìn)制數(shù)據(jù)進(jìn)行的運(yùn)算(+、-、*、/)都是叫位運(yùn)算,即將符號(hào)位共同參與運(yùn)算的運(yùn)算。

我們每一種語(yǔ)言最終都會(huì)通過(guò)編譯器轉(zhuǎn)換成機(jī)器語(yǔ)言來(lái)執(zhí)行,所以直接使用底層的語(yǔ)言就不需要便編譯器的轉(zhuǎn)換工作從而得到更高的執(zhí)行效率,當(dāng)然可讀性可能會(huì)降低,這也是為什么匯編在大部分情況下有更快的速度。項(xiàng)目中合理的運(yùn)用位運(yùn)算能提高我們代碼的執(zhí)行效率。

?

二、加減乘除運(yùn)算原理

2.1 加法和乘法

舉一個(gè)簡(jiǎn)單的例子來(lái)看下CPU是如何進(jìn)行計(jì)算的,比如這行代碼

int a = 35;int b = 47;int c = a + b;

計(jì)算兩個(gè)數(shù)的和,因?yàn)樵谟?jì)算機(jī)中都是以二進(jìn)制來(lái)進(jìn)行運(yùn)算,所以上面我們所給的int變量會(huì)在機(jī)器內(nèi)部先轉(zhuǎn)換為二進(jìn)制在進(jìn)行相加

35: 0 0 1 0 0 0 1 147: 0 0 1 0 1 1 1 1————————————————————82: 0 1 0 1 0 0 1 0

再來(lái)看下乘法,執(zhí)行如下的代碼

int a = 3; int b = 2; int c = a * b;3: 0 0 0 0 0 0 1 1 * 2————————————————————6: 0 0 0 0 0 1 1 0*********************************************int a = 3; int b = 4; int c = a * b;3: 0 0 0 0 0 0 1 1 * 4————————————————————12: 0 0 0 0 1 1 0 0*********************************************int a = 3; int b = 8; int c = a * b;3: 0 0 0 0 0 0 1 1 * 8————————————————————24: 0 0 0 1 1 0 0 0

通過(guò)以上運(yùn)算可以看出當(dāng)用a乘b,且如果b滿足2^N的時(shí)候 就相當(dāng)于把a(bǔ)的二進(jìn)制數(shù)據(jù)向左移動(dòng)N位,放到代碼中 我們可以這樣來(lái)寫(xiě) a << N,所以上面3 * 2、3 * 4、3 * 8其實(shí)是可以寫(xiě)成3<<1、3<<2、3<<3,運(yùn)算結(jié)果都是一樣的。

那假如相乘的兩個(gè)數(shù)都不滿足2N怎么辦呢?其實(shí)這個(gè)時(shí)候編譯器會(huì)將其中一個(gè)數(shù)拆分成多個(gè)滿足2N的數(shù)相加的情況,打個(gè)比方

int a = 15; int a = 15 int b = 13; => int b = (4 + 8 + 1) int c = a * b; int c = a * b

最后其實(shí)執(zhí)行相乘運(yùn)算就會(huì)變成這樣 15 * 4 + 15 * 8 + 15 * 1,按照上文說(shuō)的移位來(lái)轉(zhuǎn)換為位運(yùn)算就會(huì)變成15 << 2 + 15 << 3 + 15 << 0

?

2.2 減法和除法

減法也是與加法同理只不過(guò)計(jì)算機(jī)內(nèi)減法操作就是加上一個(gè)數(shù)的負(fù)數(shù)形式,且在操作系統(tǒng)中都是以補(bǔ)碼的形式進(jìn)行操作(因?yàn)檎龜?shù)的源碼補(bǔ)碼反碼都與本身相同)。首先, 因?yàn)槿四X可以知道第一位是符號(hào)位, 在計(jì)算的時(shí)候我們會(huì)根據(jù)符號(hào)位, 選擇對(duì)真值區(qū)域的加減. 但是對(duì)于計(jì)算機(jī), 加減乘數(shù)已經(jīng)是最基礎(chǔ)的運(yùn)算, 要設(shè)計(jì)的盡量簡(jiǎn)單. 計(jì)算機(jī)辨別"符號(hào)位"顯然會(huì)讓計(jì)算機(jī)的基礎(chǔ)電路設(shè)計(jì)變得十分復(fù)雜! 于是人們想出了將符號(hào)位也參與運(yùn)算的方法. 我們知道, 根據(jù)運(yùn)算法則減去一個(gè)正數(shù)等于加上一個(gè)負(fù)數(shù), 即: 1-1 = 1 + (-1) = 0 , 所以機(jī)器可以只有加法而沒(méi)有減法, 這樣計(jì)算機(jī)運(yùn)算的設(shè)計(jì)就更簡(jiǎn)單了.

除法的話其實(shí)和乘法原理相同,不過(guò)乘法是左移而除法是右移,但是除法的計(jì)算量要比乘法大得多,其大部分的消耗都在拆分?jǐn)?shù)值,和處理小數(shù)的步驟上,所以如果我們?cè)谶M(jìn)行生成變量的時(shí)候如果遇到多位的小數(shù)我們盡量把他換成string的形式,這也是為什么浮點(diǎn)運(yùn)算會(huì)消耗大量的時(shí)鐘周期。

操作系統(tǒng)中每進(jìn)行一個(gè)移位或者加法運(yùn)算的過(guò)程所消耗的時(shí)間就是一個(gè)時(shí)鐘周期,3.0GHz頻率的CPU可以在一秒執(zhí)行運(yùn)算3.010241024*1024個(gè)時(shí)鐘周期

?

三、位運(yùn)算

3.1 按位與 &?

針對(duì)二進(jìn)制,只要有一個(gè)為0,就為0.

public static void main(String[] args) {System.out.println("2&3運(yùn)算的結(jié)果是 :"+(2&3));//打印的結(jié)果是:?? 2&3運(yùn)算的結(jié)果是 :2}

?

3.2 按位或 |

針對(duì)二進(jìn)制,只要有一個(gè)為1,就為1.

3.3 按位異或 ^

針對(duì)二進(jìn)制,相同的為0,不同的為1

public static void main(String[] args) {System.out.println("2^3運(yùn)算的結(jié)果是 :"+(2^3));//打印的結(jié)果是: 2^3運(yùn)算的結(jié)果是 :1 } 2 =======>0010 3 =======>0011 2^3就為0001,結(jié)果就是1

3.4 取反 ~

針對(duì)二進(jìn)制,1取0,0取1

3.5 按位左移<<

針對(duì)二進(jìn)制,將操作數(shù)的所有位向左移動(dòng)指定的位數(shù)。

desc:轉(zhuǎn)換成二進(jìn)制后向左移動(dòng)3位,后面用0補(bǔ)齊public static void main(String[] args) {System.out.println("2<<3運(yùn)算的結(jié)果是 :"+(2<<3));//打印的結(jié)果是:?? 2<<3運(yùn)算的結(jié)果是 :16}

下圖展示了11111111 << 1(11111111 左移一位)的結(jié)果。

藍(lán)色數(shù)字表示被移動(dòng)位,灰色表示被丟棄位,空位用橙色的0填充。

?

3.6 按位右移 >>

針對(duì)二進(jìn)制,將操作數(shù)的所有位向又移動(dòng)指定的位數(shù)。

desc:轉(zhuǎn)換成二進(jìn)制后向右移動(dòng)3位public static void main(String[] args) {System.out.println("2>>3運(yùn)算的結(jié)果是 :"+(2>>3));//打印的結(jié)果是:?? 2>>3運(yùn)算的結(jié)果是 :0}

下圖展示了11111111 >> 1(11111111 右移一位)的結(jié)果。

藍(lán)色數(shù)字表示被移動(dòng)位,灰色表示被丟棄位,空位用橙色的0填充。

?

3.7 無(wú)符號(hào)按位右移 >>>??

無(wú)符號(hào)右移,忽略符號(hào)位,空位都以0補(bǔ)齊。

二進(jìn)制的最高位是符號(hào)位,0表示正,1表示負(fù)。

>>>與>>唯一的不同是它無(wú)論原來(lái)的最左邊是什么數(shù),統(tǒng)統(tǒng)都用0填充。

?

十進(jìn)制轉(zhuǎn)二進(jìn)制時(shí),因?yàn)槎M(jìn)制數(shù)一般分8位、 16位、32位以及64位表示一個(gè)十進(jìn)制數(shù),所以在轉(zhuǎn)換過(guò)程中,最高位會(huì)補(bǔ)零。在計(jì)算機(jī)中負(fù)數(shù)采用二進(jìn)制的補(bǔ)碼表示,十進(jìn)制轉(zhuǎn)為二進(jìn)制得到的是源碼,將源碼按位取反得到的是反碼,反碼加1得到補(bǔ)碼。

比如byte是8位的,-1表示為byte型是11111111(補(bǔ)碼表示法),-1>>>4就是無(wú)符號(hào)右移4位,即00001111,這樣結(jié)果就是15。

public static void main(String[] args) {System.out.println("16>>2運(yùn)算的結(jié)果是 :"+((16)>>2));//打印的結(jié)果是:?? 16>>2運(yùn)算的結(jié)果是 :4}public static void main(String[] args) {System.out.println("-16>>2運(yùn)算的結(jié)果是 :"+((-16)>>2));//打印的結(jié)果是:?? -16>>2運(yùn)算的結(jié)果是 :-4}public static void main(String[] args) {System.out.println("16>>>2運(yùn)算的結(jié)果是 :"+((16)>>>2));//打印的結(jié)果是:?? 16>>>2運(yùn)算的結(jié)果是 :4}public static void main(String[] args) {System.out.println("-16>>>2運(yùn)算的結(jié)果是 :"+((-16)>>>2));//打印的結(jié)果是:?? -16>>>2運(yùn)算的結(jié)果是 :1073741820}

可見(jiàn)正數(shù)做>>>運(yùn)算的時(shí)候和>>是一樣的。區(qū)別在于負(fù)數(shù)運(yùn)算

?

3.8 總結(jié)

位運(yùn)算應(yīng)用口訣?

清零取反要用與,某位置一可用或?

若要取反和交換,輕輕松松用異或?

?

移位運(yùn)算要點(diǎn)?

?1 、它們都是雙目運(yùn)算符,兩個(gè)運(yùn)算分量都是整形,結(jié)果也是整形。?

?2 、右移運(yùn)算符>>:右邊的位被擠掉,對(duì)于左邊多出的空位,如果是正數(shù)則空位補(bǔ)0,若為負(fù)數(shù),可能補(bǔ)0或補(bǔ)1,這取決于所用的計(jì)算機(jī)系統(tǒng)。?

?3、 左移運(yùn)算符<<:左邊的位被擠掉,對(duì)于右邊移出的空位一概補(bǔ)上0。?

?

位運(yùn)算符的應(yīng)用?

參數(shù):源操作數(shù)s ,掩碼mask

(1)?按位與&?

? ? 1 、清零特定位?(mask中特定位置0,其它位為1,s=s&mask)?

? ? 2、 取某數(shù)中指定位?(mask中特定位置1,其它位為0,s=s&mask)?

(2)?按位或 |?

????常用來(lái)將源操作數(shù)某些位置1,其它位不變。

?(mask中特定位置1,其它位為0?s=s?|mask)?

(3)?位異或 ^?

? ? 1、 使特定位的值取反?(mask中特定位置1,其它位為0?s=s^mask)?

? ? 2 、不引入第三變量,交換兩個(gè)變量的值

?

二進(jìn)制補(bǔ)碼運(yùn)算公式

-x = ~x + 1 = ~(x-1)

~x = -x-1

-(~x) = x+1

~(-x) = x-1

x+y = x - ~y - 1 = (x|y)+(x&y)

x-y = x + ~y + 1 = (x|~y)-(~x&y)

x^y = (x|y)-(x&y)

x|y = (x&~y)+y

x&y = (~x|y)-~x

x==y:????~(x-y|y-x)

x!=y:????x-y|y-x

x< y:????(x-y)^((x^y)&((x-y)^x))

x<=y:????(x|~y)&((x^y)|~(y-x))

x< y:????(~x&y)|((~x|y)&(x-y))//無(wú)符號(hào)x,y比較

x<=y:????(~x|y)&((x^y)|~(y-x))//無(wú)符號(hào)x,y比較

?

四、位運(yùn)算應(yīng)用

4.1 常用位運(yùn)算

(1) 判斷int型變量a是奇數(shù)還是偶數(shù)???????????

???????a&1???= 0 偶數(shù)

???????a&1 =???1 奇數(shù)

(2) 取int型變量a的第k位 (k=0,1,2……sizeof(int)),即a>>k&1

(3) 將int型變量a的第k位清0,即a=a&~(1<<k)

(4) 將int型變量a的第k位置1,即a=a|(1<<k)

(5) int型變量循環(huán)左移k次,即a=a<<k|a>>16-k???(設(shè)sizeof(int)=16)

(6) int型變量a循環(huán)右移k次,即a=a>>k|a<<16-k???(設(shè)sizeof(int)=16)

(7)整數(shù)的平均值

對(duì)于兩個(gè)整數(shù)x,y,如果用 (x+y)/2 求平均值,會(huì)產(chǎn)生溢出,因?yàn)?x+y 可能會(huì)大于INT_MAX,但是我們知道它們的平均值是肯定不會(huì)溢出的,我們用如下算法:

int average(int x, int y)???//返回X,Y 的平均值{???return (x&y)+((x^y)>>1);}boolean power2(int x){return ((x&(x-1))==0)&&(x!=0);}

(9)不用temp交換兩個(gè)整數(shù)

三種交換算法的總結(jié) t=a; a=b; b=t; 優(yōu)點(diǎn):不會(huì)溢出,可用于多種數(shù)據(jù)類(lèi)型(指針,字符串等) 缺點(diǎn):多用一個(gè)變量 a=a+b; b=a-b; a=a-b; 優(yōu)點(diǎn):不增加變量。缺點(diǎn):可能數(shù)值溢出。不能用于非數(shù)值交換

?a ^= b; b ^= a; a ^= b; (a^=b^=a^=b;) 優(yōu)點(diǎn):速度快,不會(huì)數(shù)值溢出 缺點(diǎn):只能用于整型量交換

?

(10)計(jì)算絕對(duì)值

int abs( int x ){int y ;y = x >> 31 ;return (x^y)-y ;????????//or: (x+y)^y}

(11)取模運(yùn)算轉(zhuǎn)化成位運(yùn)算 (在不產(chǎn)生溢出的情況下):a % (2^n) 等價(jià)于 a & (2^n - 1)

(12)乘法運(yùn)算轉(zhuǎn)化成位運(yùn)算 (在不產(chǎn)生溢出的情況下):a * (2^n) 等價(jià)于 a<< n

(13)除法運(yùn)算轉(zhuǎn)化成位運(yùn)算 (在不產(chǎn)生溢出的情況下):a / (2^n) 等價(jià)于 a>> n

????????例: 12/8 == 12>>3

(14) a % 2 等價(jià)于 a & 1???????

(15) if (x == a) x= b; else x= a; 等價(jià)于 x= a ^ b ^ x;

(16) x 的相反數(shù) 表示為 (~x+1)

?

4.2 顏色轉(zhuǎn)換

背景

打個(gè)比方設(shè)計(jì)師再給我們出設(shè)計(jì)稿的時(shí)候通常會(huì)在設(shè)計(jì)稿上按照16進(jìn)制的樣子給我們標(biāo)色值。但是iOS中的UIColor并不支持使用十六進(jìn)制的數(shù)據(jù)來(lái)初始化。所以我們需要將十六進(jìn)制的色值轉(zhuǎn)換為UIColor。

?

原理分析

UIColor中通常是用傳入RGB的數(shù)值來(lái)初始化,而且每個(gè)顏色的取值范圍是十進(jìn)制下的0~255,而設(shè)計(jì)同學(xué)又給的是十六進(jìn)制數(shù)據(jù),所以在操作系統(tǒng)中需要把這兩種進(jìn)制的數(shù)據(jù)統(tǒng)一成二進(jìn)制來(lái)進(jìn)行計(jì)算,這就用到了位運(yùn)算。這里用一個(gè)十六進(jìn)制的色值來(lái)舉例子比如0xffa131我們要轉(zhuǎn)換就要先理解其組成

  • 0x或者0X:十六進(jìn)制的標(biāo)識(shí)符,表示這個(gè)后面是個(gè)十六進(jìn)制的數(shù)值,對(duì)數(shù)值本身沒(méi)有任何意義
  • ff 顏色中的R值,轉(zhuǎn)換為二進(jìn)制為 1111 1111
  • a1 顏色中的G值,轉(zhuǎn)換為二進(jìn)制為 1010 0001
  • 31 顏色中的B值,轉(zhuǎn)換為二進(jìn)制為 0011 0001
  • 上述色彩值轉(zhuǎn)換為二進(jìn)制后為1111 1111 1010 0001 0011 0001(每一位十六進(jìn)制的對(duì)應(yīng)4位二進(jìn)制,如果位數(shù)不夠記得高位補(bǔ)零)

通常來(lái)講十六進(jìn)制的顏色是按照上面的RGB的順序排列的,但是并不固定,有時(shí)候可能會(huì)在其中加A(Alpha)值,具體情況按照設(shè)計(jì)為準(zhǔn),本文以通用情況舉例。

綜上,我們只需把對(duì)應(yīng)位的值轉(zhuǎn)換為10進(jìn)制然后/255.0f就可得到RGB色彩值,從而轉(zhuǎn)換為UIColor。

?

轉(zhuǎn)換代碼

先列出代碼,后續(xù)解析

- (UIColor *)colorWithHex:(long)hexColor alpha:(float)opacity {//將傳入的十六進(jìn)制顏色0xffa131 轉(zhuǎn)換為UIColorfloat red = ((hexColor & 0xFF0000) >> 16)/255.0f;float green = ((hexColor & 0xFF00) >> 8)/255.0f;float blue = (hexColor & 0xFF)/255.0f;return [UIColor colorWithRed:red green:green blue:blue alpha:opacity]; }

大概原理可以看出將RGB每個(gè)值都解析出來(lái)然后變成UIColor,先拿第一步轉(zhuǎn)換紅色值來(lái)說(shuō),我們按照運(yùn)算順序一步步來(lái)講(默認(rèn)將參數(shù)代入,用0xffa131代替hexColor)

  • 0xffa131 & 0xFF0000

我們知道紅色值是前兩位也就是ff,所以這一步我們既然要取出紅色值就要把其他位全部置零來(lái)排除干擾,這步操作便是如此,在計(jì)算機(jī)系統(tǒng)內(nèi)是二進(jìn)制來(lái)實(shí)現(xiàn)的,即:

?

1111 1111 1010 0001 0011 0001

------------------------------------------- => & => 1111 1111 0000 0000 0000

?

1111 1111 0000 0000 0000 0000

這部操作做完后可以看出將除了R值之外的G值B值全部置零了,但是離最終結(jié)果還差點(diǎn),因?yàn)?xFF是1111 1111,而我們的結(jié)果后面多出了16個(gè)0,所以便有了第二步操作

  • >> 16

將上一步得到的結(jié)果右移16位即得到0000 0000 0000 0000 1111 1111高位的零可以忽略,這也是最終的結(jié)果

  • / 255.0f

這一步應(yīng)該都知道UIColor中傳入的數(shù)值范圍在0~1,所以我們要做下轉(zhuǎn)換

  • 后續(xù)的G值和B值都是一樣的,只是大家注意位數(shù)就可以了,值得注意的是兩個(gè)二進(jìn)制數(shù)進(jìn)行位運(yùn)算一定保證兩個(gè)數(shù)的位數(shù)相同,位數(shù)不夠的那個(gè)數(shù)高位要用0補(bǔ)齊

?

4.3 枚舉

關(guān)于枚舉中使用位運(yùn)算我們之前也講過(guò),下面我們自己來(lái)寫(xiě)一個(gè)枚舉(偽代碼)

typedef NS_OPTIONS(NSUInteger, TestOptions) {TestOptionOne = 1 << 0, (000001)TestOptionTwo = 1 << 1, (000010)TestOptionThree = 1 << 2, (000100)TestOptionFour = 1 << 3, (001000)TestOptionFive = 1 << 4, (010000)TestOptionSix = 1 << 5, (100000)

上面的枚舉我后面用括號(hào)表明了位移后對(duì)應(yīng)的二進(jìn)制的值。這樣寫(xiě)枚舉的好處是我可以對(duì)其中選項(xiàng)多選比如TestOptionOne | TestOptionTwo (000001 | 000010 => 000011) 或者有其他的自定義組合。

?

4.4 加密

在iOS中我們可以利用異或來(lái)進(jìn)行加解密,異或的特性如下

A ^ B = C => C ^ A = B => C ^ B = A

上文我們可以把A認(rèn)為是需要加密的數(shù)據(jù),B認(rèn)為是密鑰 C是加密后的數(shù)據(jù),比如:

#include <stdio.h> main() {char a[]="MyPassword"; /*要加密的密碼*/char b[]="cryptographic"; /*密鑰*/int i;/*加密代碼*/for(i=0;a[i]!='\0';i++)a[i]=a[i]^b[i];printf("You Password encrypted: %s\n",a);/*解密代碼*/for(i=0;a[i]!='\0';i++)a[i]=a[i]^b[i];printf("You Password: %s\n",a); }

?

總結(jié)

以上是生活随笔為你收集整理的一文搞定位运算的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。